diff --git a/changelog.d/colorfuncs.fix b/changelog.d/colorfuncs.fix
deleted file mode 100644
index 18c49e2fc..000000000
--- a/changelog.d/colorfuncs.fix
+++ /dev/null
@@ -1 +0,0 @@
-Fix some of the color manipulation functions
diff --git a/changelog.d/drafts.add b/changelog.d/drafts.add
deleted file mode 100644
index 1147016e3..000000000
--- a/changelog.d/drafts.add
+++ /dev/null
@@ -1 +0,0 @@
-Add draft management system
diff --git a/src/App.scss b/src/App.scss
index 2afc43908..f52ba06b9 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -748,12 +748,6 @@ option {
margin-left: 0.7em;
margin-top: -1em;
}
-
- &.-neutral {
- background-color: var(--badgeNeutral);
- color: white;
- color: var(--badgeNeutralText, white);
- }
}
.alert {
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index cf242092a..65a151785 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -368,8 +368,6 @@ const afterStoreSetup = async ({ store, i18n }) => {
getInstanceConfig({ store })
]).catch(e => Promise.reject(e))
- await store.dispatch('loadDrafts')
-
// Start fetching things that don't need to block the UI
store.dispatch('fetchMutes')
store.dispatch('startFetchingAnnouncements')
diff --git a/src/boot/routes.js b/src/boot/routes.js
index 66d937c8e..f87b2ec85 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -26,7 +26,6 @@ import ListsEdit from 'components/lists_edit/lists_edit.vue'
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
import AnnouncementsPage from 'components/announcements_page/announcements_page.vue'
import QuotesTimeline from '../components/quotes_timeline/quotes_timeline.vue'
-import Drafts from 'components/drafts/drafts.vue'
import BookmarkFolders from '../components/bookmark_folders/bookmark_folders.vue'
import BookmarkFolderEdit from '../components/bookmark_folder_edit/bookmark_folder_edit.vue'
@@ -83,7 +82,6 @@ export default (store) => {
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
{ name: 'about', path: '/about', component: About },
{ name: 'announcements', path: '/announcements', component: AnnouncementsPage },
- { name: 'drafts', path: '/drafts', component: Drafts },
{ name: 'user-profile', path: '/users/:name', component: UserProfile },
{ name: 'legacy-user-profile', path: '/:name', component: UserProfile },
{ name: 'lists', path: '/lists', component: Lists },
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
index 57158abad..6efe576b8 100644
--- a/src/components/chat/chat.vue
+++ b/src/components/chat/chat.vue
@@ -76,7 +76,6 @@
:disable-sensitivity-checkbox="true"
:disable-submit="errorLoadingChat || !currentChat"
:disable-preview="true"
- :disable-draft="true"
:optimistic-posting="true"
:post-handler="sendMessage"
:submit-on-enter="!mobileLayout"
diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js
deleted file mode 100644
index cb07ec5c5..000000000
--- a/src/components/draft/draft.js
+++ /dev/null
@@ -1,64 +0,0 @@
-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'
-
-const Draft = {
- components: {
- PostStatusForm,
- EditStatusForm,
- ConfirmModal,
- StatusContent
- },
- props: {
- draft: {
- type: Object,
- required: true
- }
- },
- data () {
- return {
- editing: false,
- showingConfirmDialog: false
- }
- },
- computed: {
- relAttrs () {
- if (this.draft.type === 'edit') {
- return { statusId: this.draft.refId }
- } else if (this.draft.type === 'reply') {
- return { replyTo: this.draft.refId }
- } else {
- return {}
- }
- },
- postStatusFormProps () {
- return {
- draftId: this.draft.id,
- ...this.relAttrs
- }
- },
- refStatus () {
- return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined
- }
- },
- methods: {
- toggleEditing () {
- this.editing = !this.editing
- },
- abandon () {
- this.showingConfirmDialog = true
- },
- doAbandon () {
- this.$store.dispatch('abandonDraft', { id: this.draft.id })
- .then(() => {
- this.hideConfirmDialog()
- })
- },
- hideConfirmDialog () {
- this.showingConfirmDialog = false
- }
- }
-}
-
-export default Draft
diff --git a/src/components/draft/draft.vue b/src/components/draft/draft.vue
deleted file mode 100644
index d9d356121..000000000
--- a/src/components/draft/draft.vue
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }}
-
-
-
-
-
-
{{ draft.status }}
-
-
-
-
- {{ $t('drafts.abandon_confirm') }}
-
-
-
-
-
-
-
-
diff --git a/src/components/draft_closer/draft_closer.js b/src/components/draft_closer/draft_closer.js
deleted file mode 100644
index e50ea05ab..000000000
--- a/src/components/draft_closer/draft_closer.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import DialogModal from 'src/components/dialog_modal/dialog_modal.vue'
-
-const DraftCloser = {
- data () {
- return {
- showing: false
- }
- },
- components: {
- DialogModal
- },
- emits: [
- 'save',
- 'discard'
- ],
- computed: {
- action () {
- if (this.$store.getters.mergedConfig.autoSaveDraft) {
- return 'save'
- } else {
- return this.$store.getters.mergedConfig.unsavedPostAction
- }
- },
- shouldConfirm () {
- return this.action === 'confirm'
- }
- },
- methods: {
- requestClose () {
- if (this.shouldConfirm) {
- this.showing = true
- } else if (this.action === 'save') {
- this.save()
- } else {
- this.discard()
- }
- },
- save () {
- this.$emit('save')
- this.showing = false
- },
- discard () {
- this.$emit('discard')
- this.showing = false
- },
- cancel () {
- this.showing = false
- }
- }
-}
-
-export default DraftCloser
diff --git a/src/components/draft_closer/draft_closer.vue b/src/components/draft_closer/draft_closer.vue
deleted file mode 100644
index 1afb1f44d..000000000
--- a/src/components/draft_closer/draft_closer.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
- {{ $t('post_status.close_confirm_title') }}
-
-
-
- {{ $t('post_status.close_confirm') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/drafts/drafts.js b/src/components/drafts/drafts.js
deleted file mode 100644
index 201417f66..000000000
--- a/src/components/drafts/drafts.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import Draft from 'src/components/draft/draft.vue'
-import List from 'src/components/list/list.vue'
-
-const Drafts = {
- components: {
- Draft,
- List
- },
- computed: {
- drafts () {
- return this.$store.getters.draftsArray
- }
- }
-}
-
-export default Drafts
diff --git a/src/components/drafts/drafts.vue b/src/components/drafts/drafts.vue
deleted file mode 100644
index e64a506e5..000000000
--- a/src/components/drafts/drafts.vue
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
- {{ $t('drafts.drafts') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/components/edit_status_form/edit_status_form.js b/src/components/edit_status_form/edit_status_form.js
deleted file mode 100644
index 323763370..000000000
--- a/src/components/edit_status_form/edit_status_form.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import PostStatusForm from '../post_status_form/post_status_form.vue'
-import statusPosterService from '../../services/status_poster/status_poster.service.js'
-
-const EditStatusForm = {
- components: {
- PostStatusForm
- },
- props: {
- params: {
- type: Object,
- required: true
- }
- },
- methods: {
- requestClose () {
- this.$refs.postStatusForm.requestClose()
- },
- doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
- const params = {
- store: this.$store,
- statusId: this.params.statusId,
- status,
- spoilerText,
- sensitive,
- poll,
- media,
- contentType
- }
-
- return statusPosterService.editStatus(params)
- .then((data) => {
- return data
- })
- .catch((err) => {
- console.error('Error editing status', err)
- return {
- error: err.message
- }
- })
- }
- }
-}
-
-export default EditStatusForm
diff --git a/src/components/edit_status_form/edit_status_form.vue b/src/components/edit_status_form/edit_status_form.vue
deleted file mode 100644
index 0a7ec760a..000000000
--- a/src/components/edit_status_form/edit_status_form.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/src/components/edit_status_modal/edit_status_modal.js b/src/components/edit_status_modal/edit_status_modal.js
index 0de2e53c7..75adfea75 100644
--- a/src/components/edit_status_modal/edit_status_modal.js
+++ b/src/components/edit_status_modal/edit_status_modal.js
@@ -1,10 +1,11 @@
-import EditStatusForm from '../edit_status_form/edit_status_form.vue'
+import PostStatusForm from '../post_status_form/post_status_form.vue'
import Modal from '../modal/modal.vue'
+import statusPosterService from '../../services/status_poster/status_poster.service.js'
import get from 'lodash/get'
const EditStatusModal = {
components: {
- EditStatusForm,
+ PostStatusForm,
Modal
},
data () {
@@ -42,10 +43,30 @@ const EditStatusModal = {
}
},
methods: {
- closeModal () {
- this.$refs.editStatusForm.requestClose()
+ doEditStatus ({ status, spoilerText, sensitive, media, contentType, poll }) {
+ const params = {
+ store: this.$store,
+ statusId: this.$store.state.editStatus.params.statusId,
+ status,
+ spoilerText,
+ sensitive,
+ poll,
+ media,
+ contentType
+ }
+
+ return statusPosterService.editStatus(params)
+ .then((data) => {
+ return data
+ })
+ .catch((err) => {
+ console.error('Error editing status', err)
+ return {
+ error: err.message
+ }
+ })
},
- doCloseModal () {
+ closeModal () {
this.$store.dispatch('closeEditStatusModal')
}
}
diff --git a/src/components/edit_status_modal/edit_status_modal.vue b/src/components/edit_status_modal/edit_status_modal.vue
index 42073d7d3..5001bd46c 100644
--- a/src/components/edit_status_modal/edit_status_modal.vue
+++ b/src/components/edit_status_modal/edit_status_modal.vue
@@ -10,12 +10,13 @@
{{ $t('post_status.edit_status') }}
-
diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js
index bc962e32f..11863e975 100644
--- a/src/components/nav_panel/nav_panel.js
+++ b/src/components/nav_panel/nav_panel.js
@@ -20,8 +20,7 @@ import {
faInfoCircle,
faStream,
faList,
- faBullhorn,
- faFilePen
+ faBullhorn
} from '@fortawesome/free-solid-svg-icons'
library.add(
@@ -36,8 +35,7 @@ library.add(
faInfoCircle,
faStream,
faList,
- faBullhorn,
- faFilePen
+ faBullhorn
)
const NavPanel = {
props: ['forceExpand', 'forceEditMode'],
diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js
index 46ed3f8c2..156cd8cb3 100644
--- a/src/components/navigation/navigation.js
+++ b/src/components/navigation/navigation.js
@@ -62,7 +62,6 @@ export const ROOT_ITEMS = {
route: 'chats',
icon: 'comments',
label: 'nav.chats',
- badgeStyle: 'notification',
badgeGetter: 'unreadChatCount',
criteria: ['chats']
},
@@ -70,7 +69,6 @@ export const ROOT_ITEMS = {
route: 'friend-requests',
icon: 'user-plus',
label: 'nav.friend_requests',
- badgeStyle: 'notification',
criteria: ['lockedUser'],
badgeGetter: 'followRequestCount'
},
@@ -84,16 +82,8 @@ export const ROOT_ITEMS = {
route: 'announcements',
icon: 'bullhorn',
label: 'nav.announcements',
- badgeStyle: 'notification',
badgeGetter: 'unreadAnnouncementCount',
criteria: ['announcements']
- },
- drafts: {
- route: 'drafts',
- icon: 'file-pen',
- label: 'nav.drafts',
- badgeStyle: 'neutral',
- badgeGetter: 'draftCount'
}
}
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
index 4b6c8e299..024ee3148 100644
--- a/src/components/navigation/navigation_entry.vue
+++ b/src/components/navigation/navigation_entry.vue
@@ -48,8 +48,7 @@
{{ getters[item.badgeGetter] }}
diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue
index 37351b918..decd1c04b 100644
--- a/src/components/navigation/navigation_pins.vue
+++ b/src/components/navigation/navigation_pins.vue
@@ -19,8 +19,7 @@
>{{ item.iconLetter }}
@@ -35,14 +34,6 @@
overflow: hidden;
height: 100%;
- &.alert-dot-notification {
- background-color: var(--badgeNotification);
- }
-
- &.alert-dot-neutral {
- background-color: var(--badgeNeutral);
- }
-
.pinned-item {
position: relative;
flex: 1 0 3em;
diff --git a/src/components/poll/poll_form.js b/src/components/poll/poll_form.js
index 29ccbc4bf..a20701553 100644
--- a/src/components/poll/poll_form.js
+++ b/src/components/poll/poll_form.js
@@ -1,5 +1,5 @@
import * as DateUtils from 'src/services/date_utils/date_utils.js'
-import { pollFallback } from 'src/services/poll/poll.service.js'
+import { uniq } from 'lodash'
import { library } from '@fortawesome/fontawesome-svg-core'
import Select from '../select/select.vue'
import {
@@ -17,33 +17,14 @@ export default {
Select
},
name: 'PollForm',
- props: {
- visible: {},
- params: {
- type: Object,
- required: true
- }
- },
+ props: ['visible'],
+ data: () => ({
+ pollType: 'single',
+ options: ['', ''],
+ expiryAmount: 10,
+ expiryUnit: 'minutes'
+ }),
computed: {
- pollType: {
- get () { return pollFallback(this.params, 'pollType') },
- set (newVal) { this.params.pollType = newVal }
- },
- options () {
- const hasOptions = !!this.params.options
- if (!hasOptions) {
- this.params.options = pollFallback(this.params, 'options')
- }
- return this.params.options
- },
- expiryAmount: {
- get () { return pollFallback(this.params, 'expiryAmount') },
- set (newVal) { this.params.expiryAmount = newVal }
- },
- expiryUnit: {
- get () { return pollFallback(this.params, 'expiryUnit') },
- set (newVal) { this.params.expiryUnit = newVal }
- },
pollLimits () {
return this.$store.state.instance.pollLimits
},
@@ -108,6 +89,7 @@ export default {
deleteOption (index, event) {
if (this.options.length > 2) {
this.options.splice(index, 1)
+ this.updatePollToParent()
}
},
convertExpiryToUnit (unit, amount) {
@@ -122,6 +104,24 @@ export default {
Math.max(this.minExpirationInCurrentUnit, this.expiryAmount)
this.expiryAmount =
Math.min(this.maxExpirationInCurrentUnit, this.expiryAmount)
+ this.updatePollToParent()
+ },
+ updatePollToParent () {
+ const expiresIn = this.convertExpiryFromUnit(
+ this.expiryUnit,
+ this.expiryAmount
+ )
+
+ const options = uniq(this.options.filter(option => option !== ''))
+ if (options.length < 2) {
+ this.$emit('update-poll', { error: this.$t('polls.not_enough_options') })
+ return
+ }
+ this.$emit('update-poll', {
+ options,
+ multiple: this.pollType === 'multiple',
+ expiresIn
+ })
}
}
}
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 1ae985498..563dfb966 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -10,13 +10,11 @@ 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 { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
-import { pollFormToMasto } from 'src/services/poll/poll.service.js'
import { reject, map, uniqBy, debounce } from 'lodash'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters, mapState } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
import Select from '../select/select.vue'
-import DraftCloser from 'src/components/draft_closer/draft_closer.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@@ -57,18 +55,6 @@ const pxStringToNumber = (str) => {
return Number(str.substring(0, str.length - 2))
}
-const typeAndRefId = ({ replyTo, profileMention, statusId }) => {
- if (replyTo) {
- return ['reply', replyTo]
- } else if (profileMention) {
- return ['mention', profileMention]
- } else if (statusId) {
- return ['edit', statusId]
- } else {
- return ['new', '']
- }
-}
-
const PostStatusForm = {
props: [
'statusId',
@@ -93,7 +79,6 @@ const PostStatusForm = {
'disableSensitivityCheckbox',
'disableSubmit',
'disablePreview',
- 'disableDraft',
'placeholder',
'maxHeight',
'postHandler',
@@ -103,15 +88,13 @@ const PostStatusForm = {
'submitOnEnter',
'emojiPickerPlacement',
'optimisticPosting',
- 'profileMention',
- 'draftId'
+ 'profileMention'
],
emits: [
'posted',
'resize',
'mediaplay',
- 'mediapause',
- 'can-close'
+ 'mediapause'
],
components: {
MediaUpload,
@@ -122,8 +105,7 @@ const PostStatusForm = {
Select,
Attachment,
StatusContent,
- Gallery,
- DraftCloser
+ Gallery
},
mounted () {
this.updateIdempotencyKey()
@@ -144,54 +126,41 @@ const PostStatusForm = {
const { scopeCopy } = this.$store.getters.mergedConfig
- const [statusType, refId] = typeAndRefId({ replyTo: this.replyTo, profileMention: this.profileMention && this.repliedUser?.id, statusId: this.statusId })
+ if (this.replyTo || this.profileMention) {
+ const currentUser = this.$store.state.users.currentUser
+ statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
+ }
- // If we are starting a new post, do not associate it with old drafts
- let statusParams = !this.disableDraft && (this.draftId || statusType !== 'new') ? this.getDraft(statusType, refId) : null
+ const scope = ((this.copyMessageScope && scopeCopy) || this.copyMessageScope === 'direct')
+ ? this.copyMessageScope
+ : this.$store.state.users.currentUser.default_scope
- if (!statusParams) {
- if (statusType === 'reply' || statusType === 'mention') {
- const currentUser = this.$store.state.users.currentUser
- statusText = buildMentionsString({ user: this.repliedUser, attentions: this.attentions }, currentUser)
- }
+ const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
- const scope = ((this.copyMessageScope && scopeCopy) || this.copyMessageScope === 'direct')
- ? this.copyMessageScope
- : this.$store.state.users.currentUser.default_scope
-
- const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig
+ let statusParams = {
+ spoilerText: this.subject || '',
+ status: statusText,
+ nsfw: !!sensitiveByDefault,
+ files: [],
+ poll: {},
+ mediaDescriptions: {},
+ visibility: scope,
+ contentType
+ }
+ if (this.statusId) {
+ const statusContentType = this.statusContentType || contentType
statusParams = {
- type: statusType,
- refId,
spoilerText: this.subject || '',
- status: statusText,
- nsfw: !!sensitiveByDefault,
- files: [],
- poll: {},
- hasPoll: false,
- mediaDescriptions: {},
- visibility: scope,
- contentType,
+ status: this.statusText || '',
+ nsfw: this.statusIsSensitive || !!sensitiveByDefault,
+ files: this.statusFiles || [],
+ poll: this.statusPoll || {},
+ mediaDescriptions: this.statusMediaDescriptions || {},
+ visibility: this.statusScope || scope,
+ contentType: statusContentType,
quoting: false
}
-
- if (statusType === 'edit') {
- const statusContentType = this.statusContentType || contentType
- statusParams = {
- type: statusType,
- refId,
- spoilerText: this.subject || '',
- status: this.statusText || '',
- nsfw: this.statusIsSensitive || !!sensitiveByDefault,
- files: this.statusFiles || [],
- poll: this.statusPoll || {},
- hasPoll: false,
- mediaDescriptions: this.statusMediaDescriptions || {},
- visibility: this.statusScope || scope,
- contentType: statusContentType
- }
- }
}
return {
@@ -203,14 +172,13 @@ const PostStatusForm = {
highlighted: 0,
newStatus: statusParams,
caret: 0,
+ pollFormVisible: false,
showDropIcon: 'hide',
dropStopTimeout: null,
preview: null,
previewLoading: false,
emojiInputShown: false,
- idempotencyKey: '',
- saveInhibited: true,
- savable: false
+ idempotencyKey: ''
}
},
computed: {
@@ -325,24 +293,6 @@ const PostStatusForm = {
return false
},
- debouncedMaybeAutoSaveDraft () {
- return debounce(this.maybeAutoSaveDraft, 3000)
- },
- pollFormVisible () {
- return this.newStatus.hasPoll
- },
- shouldAutoSaveDraft () {
- return this.$store.getters.mergedConfig.autoSaveDraft
- },
- autoSaveState () {
- if (this.savable) {
- return this.$t('post_status.auto_save_saving')
- } else if (this.newStatus.id) {
- return this.$t('post_status.auto_save_saved')
- } else {
- return this.$t('post_status.auto_save_nothing_new')
- }
- },
...mapGetters(['mergedConfig']),
...mapState({
mobileLayout: state => state.interface.mobileLayout
@@ -354,32 +304,15 @@ const PostStatusForm = {
handler () {
this.statusChanged()
}
- },
- savable (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) {
- this.addBeforeUnloadListener()
- } else {
- this.removeBeforeUnloadListener()
- }
}
},
- beforeUnmount () {
- this.maybeAutoSaveDraft()
- this.removeBeforeUnloadListener()
- },
methods: {
statusChanged () {
this.autoPreview()
this.updateIdempotencyKey()
- this.debouncedMaybeAutoSaveDraft()
- this.savable = true
- this.saveInhibited = false
},
clearStatus () {
const newStatus = this.newStatus
- this.saveInhibited = true
this.newStatus = {
status: '',
spoilerText: '',
@@ -387,10 +320,10 @@ const PostStatusForm = {
visibility: newStatus.visibility,
contentType: newStatus.contentType,
poll: {},
- hasPoll: false,
mediaDescriptions: {},
quoting: false
}
+ this.pollFormVisible = false
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
this.clearPollForm()
if (this.preserveFocus) {
@@ -403,7 +336,6 @@ const PostStatusForm = {
el.style.height = undefined
this.error = null
if (this.preview) this.previewStatus()
- this.savable = false
},
async postStatus (event, newStatus, opts = {}) {
if (this.posting && !this.optimisticPosting) { return }
@@ -421,7 +353,7 @@ const PostStatusForm = {
return
}
- const poll = this.newStatus.hasPoll ? pollFormToMasto(this.newStatus.poll) : {}
+ const poll = this.pollFormVisible ? this.newStatus.poll : {}
if (this.pollContentError) {
this.error = this.pollContentError
return
@@ -456,7 +388,6 @@ const PostStatusForm = {
postHandler(postingOptions).then((data) => {
if (!data.error) {
- this.abandonDraft()
this.clearStatus()
this.$emit('posted', data)
} else {
@@ -701,7 +632,7 @@ const PostStatusForm = {
this.newStatus.visibility = visibility
},
togglePollForm () {
- this.newStatus.hasPoll = !this.newStatus.hasPoll
+ this.pollFormVisible = !this.pollFormVisible
},
setPoll (poll) {
this.newStatus.poll = poll
@@ -734,78 +665,6 @@ const PostStatusForm = {
},
propsToNative (props) {
return propsToNative(props)
- },
- saveDraft () {
- if (!this.disableDraft &&
- !this.saveInhibited) {
- if (this.newStatus.status ||
- this.newStatus.files?.length ||
- this.newStatus.hasPoll) {
- return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
- .then(id => {
- if (this.newStatus.id !== id) {
- this.newStatus.id = id
- }
- this.savable = 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
- })
- }
- }
- return Promise.resolve()
- },
- maybeAutoSaveDraft () {
- if (this.shouldAutoSaveDraft) {
- this.saveDraft()
- }
- },
- abandonDraft () {
- return this.$store.dispatch('abandonDraft', { id: this.newStatus.id })
- },
- getDraft (statusType, refId) {
- const maybeDraft = this.$store.state.drafts.drafts[this.draftId]
- if (this.draftId && maybeDraft) {
- return maybeDraft
- } else {
- const existingDrafts = this.$store.getters.draftsByTypeAndRefId(statusType, refId)
-
- if (existingDrafts.length) {
- return existingDrafts[0]
- }
- }
- // No draft available, fall back
- },
- requestClose () {
- if (!this.savable) {
- this.$emit('can-close')
- } else {
- this.$refs.draftCloser.requestClose()
- }
- },
- saveAndCloseDraft () {
- this.saveDraft().then(() => {
- this.$emit('can-close')
- })
- },
- discardAndCloseDraft () {
- this.abandonDraft().then(() => {
- this.$emit('can-close')
- })
- },
- addBeforeUnloadListener () {
- this._beforeUnloadListener ||= () => {
- this.saveDraft()
- }
- window.addEventListener('beforeunload', this._beforeUnloadListener)
- },
- removeBeforeUnloadListener () {
- if (this._beforeUnloadListener) {
- window.removeEventListener('beforeunload', this._beforeUnloadListener)
- }
}
}
}
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index b7a169c58..e76b52a1c 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -262,7 +262,7 @@
v-if="pollsAvailable"
ref="pollForm"
:visible="pollFormVisible"
- :params="newStatus.poll"
+ @update-poll="setPoll"
/>
-
- {{ autoSaveState }}
-
-
+
+
+
+ {{ $t('status.reply_to') }}
+
+
@@ -622,15 +609,13 @@
class="status-container reply-form"
>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index b4e56bc11..874620ddd 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -192,8 +192,7 @@
"mobile_notifications_close": "Close notifications",
"mobile_notifications_mark_as_seen": "Mark all as seen",
"announcements": "Announcements",
- "quotes": "Quotes",
- "drafts": "Drafts"
+ "quotes": "Quotes"
},
"notifications": {
"broken_favorite": "Unknown status, searching for it…",
@@ -312,16 +311,7 @@
"private": "Followers-only - post to followers only",
"public": "Public - post to public timelines",
"unlisted": "Unlisted - do not post to public timelines"
- },
- "close_confirm_title": "Closing post form",
- "close_confirm": "What do you want to do with your current writing?",
- "close_confirm_save_button": "Save",
- "close_confirm_discard_button": "Discard",
- "close_confirm_continue_composing_button": "Continue composing",
- "auto_save_nothing_new": "Nothing new to save.",
- "auto_save_saved": "Saved.",
- "auto_save_saving": "Saving...",
- "save_to_drafts_button": "Save to drafts"
+ }
},
"registration": {
"bio_optional": "Bio (optional)",
@@ -518,11 +508,6 @@
"avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.",
"pad_emoji": "Pad emoji with spaces when adding from picker",
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
- "unsaved_post_action": "When you try to close an unsaved posting form",
- "unsaved_post_action_save": "Save it to drafts",
- "unsaved_post_action_discard": "Discard it",
- "unsaved_post_action_confirm": "Ask every time",
- "auto_save_draft": "Save drafts as you compose",
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
"emoji_reactions_scale": "Reactions scale factor",
"absolute_time_format": "Use absolute time format",
@@ -1233,8 +1218,6 @@
"delete_confirm_accept_button": "Delete",
"delete_confirm_cancel_button": "Keep",
"reply_to": "Reply to",
- "reply_to_with_icon": "{icon} {replyTo}",
- "reply_to_with_arg": "{replyToWithIcon} {user}",
"mentions": "Mentions",
"replies_list": "Replies:",
"replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):",
@@ -1511,18 +1494,6 @@
"unicode_domain_indicator": {
"tooltip": "This domain contains non-ascii characters."
},
- "drafts": {
- "drafts": "Drafts",
- "continue": "Continue composing",
- "abandon": "Abandon draft",
- "abandon_confirm_title": "Abandon confirmation",
- "abandon_confirm": "Do you really want to abandon this draft?",
- "abandon_confirm_accept_button": "Abandon",
- "abandon_confirm_cancel_button": "Keep",
- "replying": "Replying to {statusLink}",
- "editing": "Editing {statusLink}",
- "unavailable": "(unavailable)"
- },
"splash": {
"loading": "Loading...",
"theme": "Applying theme, please wait warmly...",
diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js
index 035eee78d..22ba1e7f0 100644
--- a/src/lib/persisted_state.js
+++ b/src/lib/persisted_state.js
@@ -1,6 +1,6 @@
import merge from 'lodash.merge'
+import localforage from 'localforage'
import { each, get, set, cloneDeep } from 'lodash'
-import { storage } from './storage.js'
let loaded = false
@@ -26,7 +26,7 @@ const saveImmedeatelyActions = [
]
const defaultStorage = (() => {
- return storage
+ return localforage
})()
export default function createPersistedState ({
diff --git a/src/lib/storage.js b/src/lib/storage.js
deleted file mode 100644
index 25a49abc9..000000000
--- a/src/lib/storage.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import localforage from 'localforage'
-
-export const storage = localforage
diff --git a/src/main.js b/src/main.js
index 6b634e742..eee463cc7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -24,7 +24,6 @@ import pollsModule from './modules/polls.js'
import postStatusModule from './modules/postStatus.js'
import editStatusModule from './modules/editStatus.js'
import statusHistoryModule from './modules/statusHistory.js'
-import draftsModule from './modules/drafts.js'
import chatsModule from './modules/chats.js'
import announcementsModule from './modules/announcements.js'
import bookmarkFoldersModule from './modules/bookmark_folders.js'
@@ -125,7 +124,6 @@ const persistedStateOptions = {
postStatus: postStatusModule,
editStatus: editStatusModule,
statusHistory: statusHistoryModule,
- drafts: draftsModule,
chats: chatsModule,
announcements: announcementsModule,
bookmarkFolders: bookmarkFoldersModule
diff --git a/src/modules/config.js b/src/modules/config.js
index e1e395855..72db9de03 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -30,8 +30,7 @@ export const multiChoiceProperties = [
'conversationDisplay', // tree | linear
'conversationOtherRepliesButton', // below | inside
'mentionLinkDisplay', // short | full_for_remote | full
- 'userPopoverAvatarAction', // close | zoom | open
- 'unsavedPostAction' // save | discard | confirm
+ 'userPopoverAvatarAction' // close | zoom | open
]
export const defaultState = {
@@ -186,8 +185,6 @@ export const defaultState = {
closingDrawerMarksAsSeen: undefined, // instance default
unseenAtTop: undefined, // instance default
ignoreInactionableSeen: undefined, // instance default
- unsavedPostAction: undefined, // instance default
- autoSaveDraft: undefined, // instance default
useAbsoluteTimeFormat: undefined, // instance default
absoluteTimeFormatMinAge: undefined // instance default
}
diff --git a/src/modules/drafts.js b/src/modules/drafts.js
deleted file mode 100644
index 7a38fe2d5..000000000
--- a/src/modules/drafts.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { storage } from 'src/lib/storage.js'
-
-export const defaultState = {
- drafts: {}
-}
-
-export const mutations = {
- addOrSaveDraft (state, { draft }) {
- state.drafts[draft.id] = draft
- },
- abandonDraft (state, { id }) {
- delete state.drafts[id]
- },
- loadDrafts (state, data) {
- state.drafts = data
- }
-}
-
-const storageKey = 'pleroma-fe-drafts'
-
-/*
- * Note: we do not use the persist state plugin because
- * it is not impossible for a user to have two windows at
- * the same time. The persist state plugin is just overriding
- * everything with the current state. This isn't good because
- * if a draft is created in one window and another draft is
- * created in another, the draft in the first window will just
- * be overriden.
- * Here, we can't guarantee 100% atomicity unless one uses
- * different keys, which will just pollute the whole storage.
- * It is indeed best to have backend support for this.
- */
-const getStorageData = async () => ((await storage.getItem(storageKey)) || {})
-
-const saveDraftToStorage = async (draft) => {
- const currentData = await getStorageData()
- currentData[draft.id] = JSON.parse(JSON.stringify(draft))
- await storage.setItem(storageKey, currentData)
-}
-
-const deleteDraftFromStorage = async (id) => {
- const currentData = await getStorageData()
- delete currentData[id]
- await storage.setItem(storageKey, currentData)
-}
-
-export const actions = {
- async addOrSaveDraft (store, { draft }) {
- const id = draft.id || (new Date().getTime()).toString()
- const draftWithId = { ...draft, id }
- store.commit('addOrSaveDraft', { draft: draftWithId })
- await saveDraftToStorage(draftWithId)
- return id
- },
- async abandonDraft (store, { id }) {
- store.commit('abandonDraft', { id })
- await deleteDraftFromStorage(id)
- },
- async loadDrafts (store) {
- const currentData = await getStorageData()
- store.commit('loadDrafts', currentData)
- }
-}
-
-export const getters = {
- draftsByTypeAndRefId (state) {
- return (type, refId) => {
- return Object.values(state.drafts).filter(draft => draft.type === type && draft.refId === refId)
- }
- },
- draftsArray (state) {
- return Object.values(state.drafts)
- },
- draftCount (state) {
- return Object.values(state.drafts).length
- }
-}
-
-const drafts = {
- state: defaultState,
- mutations,
- getters,
- actions
-}
-
-export default drafts
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 72e34628e..23d022c6f 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -124,8 +124,6 @@ const defaultState = {
closingDrawerMarksAsSeen: true,
unseenAtTop: false,
ignoreInactionableSeen: false,
- unsavedPostAction: 'confirm',
- autoSaveDraft: false,
useAbsoluteTimeFormat: false,
absoluteTimeFormatMinAge: '0d',
diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js
index 15680c7de..d92bbbe05 100644
--- a/src/services/color_convert/color_convert.js
+++ b/src/services/color_convert/color_convert.js
@@ -52,6 +52,15 @@ const c2linear = (bit) => {
}
}
+/**
+ * Converts sRGB into linear RGB
+ * @param {Object} srgb - sRGB color
+ * @returns {Object} linear rgb color
+ */
+const srgbToLinear = (srgb) => {
+ return 'rgb'.split('').reduce((acc, c) => { acc[c] = c2linear(srgb[c]); return acc }, {})
+}
+
/**
* Calculates relative luminance for given color
* https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
@@ -61,10 +70,7 @@ const c2linear = (bit) => {
* @returns {Number} relative luminance
*/
export const relativeLuminance = (srgb) => {
- const r = c2linear(srgb.r)
- const g = c2linear(srgb.g)
- const b = c2linear(srgb.b)
-
+ const { r, g, b } = srgbToLinear(srgb)
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
@@ -104,17 +110,13 @@ export const getContrastRatioLayers = (text, layers, bedrock) => {
* @returns {Object} sRGB of resulting color
*/
export const alphaBlend = (fg, fga, bg) => {
- if (fga === 1 || typeof fga === 'undefined') {
- return fg
- }
-
- // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
- // for opaque bg and transparent fg
- return {
- r: (fg.r * fga + bg.r * (1 - fga)),
- g: (fg.g * fga + bg.g * (1 - fga)),
- b: (fg.b * fga + bg.b * (1 - fga))
- }
+ if (fga === 1 || typeof fga === 'undefined') return fg
+ return 'rgb'.split('').reduce((acc, c) => {
+ // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
+ // for opaque bg and transparent fg
+ acc[c] = (fg[c] * fga + bg[c] * (1 - fga))
+ return acc
+ }, {})
}
/**
@@ -128,11 +130,10 @@ export const alphaBlendLayers = (bedrock, layers) => layers.reduce((acc, [color,
}, bedrock)
export const invert = (rgb) => {
- return {
- r: 255 - rgb.r,
- g: 255 - rgb.g,
- b: 255 - rgb.b
- }
+ return 'rgb'.split('').reduce((acc, c) => {
+ acc[c] = 255 - rgb[c]
+ return acc
+ }, {})
}
/**
@@ -143,7 +144,6 @@ export const invert = (rgb) => {
*/
export const hex2rgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
-
return result
? {
r: parseInt(result[1], 16),
@@ -161,13 +161,11 @@ export const hex2rgb = (hex) => {
* @returns {Object} result
*/
export const mixrgb = (a, b) => {
- return {
- r: (a.r + b.r) / 2,
- g: (a.g + b.g) / 2,
- b: (a.b + b.b) / 2
- }
+ return 'rgb'.split('').reduce((acc, k) => {
+ acc[k] = (a[k] + b[k]) / 2
+ return acc
+ }, {})
}
-
/**
* Converts rgb object into a CSS rgba() color
*
@@ -175,33 +173,7 @@ export const mixrgb = (a, b) => {
* @returns {String} CSS rgba() color
*/
export const rgba2css = function (rgba) {
- const base = {
- r: 0,
- g: 0,
- b: 0,
- a: 1
- }
-
- if (rgba !== null) {
- if (rgba.r !== undefined && !isNaN(rgba.r)) {
- base.r = rgba.r
- }
- if (rgba.g !== undefined && !isNaN(rgba.g)) {
- base.g = rgba.g
- }
- if (rgba.b !== undefined && !isNaN(rgba.b)) {
- base.b = rgba.b
- }
- if (rgba.a !== undefined && !isNaN(rgba.a)) {
- base.a = rgba.a
- }
- } else {
- base.r = 255
- base.g = 255
- base.b = 255
- }
-
- return `rgba(${Math.floor(base.r)}, ${Math.floor(base.g)}, ${Math.floor(base.b)}, ${base.a})`
+ return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a ?? 1})`
}
/**
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index cbadac69f..550543e0b 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -442,9 +442,7 @@ export const parseNotification = (data) => {
if (masto) {
output.type = mastoDict[data.type] || data.type
output.seen = data.pleroma.is_seen
- // TODO: null check should be a temporary fix, I guess.
- // Investigate why backend does this.
- output.status = isStatusNotification(output.type) && data.status !== null ? parseStatus(data.status) : null
+ output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null
output.target = output.type !== 'move'
? null
: parseUser(data.target)
diff --git a/src/services/poll/poll.service.js b/src/services/poll/poll.service.js
deleted file mode 100644
index 90f8e87ae..000000000
--- a/src/services/poll/poll.service.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as DateUtils from 'src/services/date_utils/date_utils.js'
-import { uniq } from 'lodash'
-
-const pollFallbackValues = {
- pollType: 'single',
- options: ['', ''],
- expiryAmount: 10,
- expiryUnit: 'minutes'
-}
-
-const pollFallback = (object, attr) => {
- return object[attr] !== undefined ? object[attr] : pollFallbackValues[attr]
-}
-
-const pollFormToMasto = (poll) => {
- const expiresIn = DateUtils.unitToSeconds(
- pollFallback(poll, 'expiryUnit'),
- pollFallback(poll, 'expiryAmount')
- )
-
- const options = uniq(pollFallback(poll, 'options').filter(option => option !== ''))
- if (options.length < 2) {
- return { errorKey: 'polls.not_enough_options' }
- }
-
- return {
- options,
- multiple: pollFallback(poll, 'pollType') === 'multiple',
- expiresIn
- }
-}
-
-export {
- pollFallback,
- pollFormToMasto
-}
diff --git a/src/sw.js b/src/sw.js
index 4a36672c1..8d790446a 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -1,6 +1,6 @@
/* eslint-env serviceworker */
-import { storage } from 'src/lib/storage.js'
+import localForage from 'localforage'
import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js'
import { prepareNotificationObject } from './services/notification_utils/notification_utils.js'
import { createI18n } from 'vue-i18n'
@@ -25,7 +25,7 @@ function getWindowClients () {
}
const setSettings = async () => {
- const vuexState = await storage.getItem('vuex-lz')
+ const vuexState = await localForage.getItem('vuex-lz')
const locale = vuexState.config.interfaceLanguage || 'en'
i18n.locale = locale
const notificationsNativeArray = Object.entries(vuexState.config.notificationNative)