diff --git a/routes/_actions/compose.js b/routes/_actions/compose.js
index 81d432ac..82e5565a 100644
--- a/routes/_actions/compose.js
+++ b/routes/_actions/compose.js
@@ -22,7 +22,7 @@ export async function insertHandleForReply (statusId) {
export async function postStatus (realm, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility,
- mediaDescriptions = [], inReplyToUuid) {
+ mediaDescriptions, inReplyToUuid) {
let { currentInstance, accessToken, online } = store.get()
if (!online) {
@@ -30,6 +30,9 @@ export async function postStatus (realm, text, inReplyToId, mediaIds,
return
}
+ text = text || ''
+ mediaDescriptions = mediaDescriptions || []
+
store.set({
postingStatus: true
})
diff --git a/routes/_actions/media.js b/routes/_actions/media.js
index 1a3808f7..b16f8b78 100644
--- a/routes/_actions/media.js
+++ b/routes/_actions/media.js
@@ -11,13 +11,11 @@ export async function doMediaUpload (realm, file) {
let composeMedia = store.getComposeData(realm, 'media') || []
composeMedia.push({
data: response,
- file: { name: file.name }
+ file: { name: file.name },
+ description: ''
})
- let composeText = store.getComposeData(realm, 'text') || ''
- composeText += ' ' + response.text_url
store.setComposeData(realm, {
- media: composeMedia,
- text: composeText
+ media: composeMedia
})
scheduleIdleTask(() => store.save())
} catch (e) {
@@ -30,20 +28,10 @@ export async function doMediaUpload (realm, file) {
export function deleteMedia (realm, i) {
let composeMedia = store.getComposeData(realm, 'media')
- let deletedMedia = composeMedia.splice(i, 1)[0]
-
- let composeText = store.getComposeData(realm, 'text') || ''
- composeText = composeText.replace(' ' + deletedMedia.data.text_url, '')
-
- let mediaDescriptions = store.getComposeData(realm, 'mediaDescriptions') || []
- if (mediaDescriptions[i]) {
- mediaDescriptions[i] = null
- }
+ composeMedia.splice(i, 1)
store.setComposeData(realm, {
- media: composeMedia,
- text: composeText,
- mediaDescriptions: mediaDescriptions
+ media: composeMedia
})
scheduleIdleTask(() => store.save())
}
diff --git a/routes/_api/statuses.js b/routes/_api/statuses.js
index 12f6ea9b..73d2605e 100644
--- a/routes/_api/statuses.js
+++ b/routes/_api/statuses.js
@@ -16,7 +16,8 @@ export async function postStatus (instanceName, accessToken, text, inReplyToId,
for (let key of Object.keys(body)) {
let value = body[key]
- if (!value || (Array.isArray(value) && !value.length)) {
+ // remove any unnecessary fields, except 'status' which must at least be an empty string
+ if (key !== 'status' && (!value || (Array.isArray(value) && !value.length))) {
delete body[key]
}
}
diff --git a/routes/_components/compose/ComposeBox.html b/routes/_components/compose/ComposeBox.html
index 13bb87ce..ff1c182c 100644
--- a/routes/_components/compose/ComposeBox.html
+++ b/routes/_components/compose/ComposeBox.html
@@ -13,7 +13,7 @@
-
+
@@ -186,8 +186,7 @@
overLimit: ({ length, $maxStatusChars }) => length > $maxStatusChars,
contentWarningShown: ({ composeData }) => composeData.contentWarningShown,
contentWarning: ({ composeData }) => composeData.contentWarning || '',
- timelineInitialized: ({ $timelineInitialized }) => $timelineInitialized,
- mediaDescriptions: ({ composeData }) => composeData.mediaDescriptions || []
+ timelineInitialized: ({ $timelineInitialized }) => $timelineInitialized
},
transitions: {
slide
@@ -214,14 +213,14 @@
contentWarning,
realm,
overLimit,
- mediaDescriptions,
inReplyToUuid
} = this.get()
let sensitive = media.length && !!contentWarning
let mediaIds = media.map(_ => _.data.id)
+ let mediaDescriptions = media.map(_ => _.description)
let inReplyTo = (realm === 'home' || realm === 'dialog') ? null : realm
- if (!text || overLimit) {
+ if (overLimit || (!text && !media.length)) {
return // do nothing if invalid
}
diff --git a/routes/_components/compose/ComposeMedia.html b/routes/_components/compose/ComposeMedia.html
index c1d57ceb..64ad35db 100644
--- a/routes/_components/compose/ComposeMedia.html
+++ b/routes/_components/compose/ComposeMedia.html
@@ -1,7 +1,7 @@
{#if media.length}
{#each media as mediaItem, index}
-
+
{/each}
{/if}
diff --git a/routes/_components/compose/ComposeMediaItem.html b/routes/_components/compose/ComposeMediaItem.html
index 56f5ba86..4354f998 100644
--- a/routes/_components/compose/ComposeMediaItem.html
+++ b/routes/_components/compose/ComposeMediaItem.html
@@ -95,10 +95,10 @@
methods: {
observe,
setupSyncFromStore () {
- this.observe('mediaDescriptions', mediaDescriptions => {
- mediaDescriptions = mediaDescriptions || []
+ this.observe('media', media => {
+ media = media || []
let { index, rawText } = this.get()
- let text = mediaDescriptions[index] || ''
+ let text = (media[index] && media[index].description) || ''
if (rawText !== text) {
this.set({rawText: text})
}
@@ -108,17 +108,12 @@
const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
this.observe('rawText', rawText => {
- let { realm } = this.get()
- let { index } = this.get()
- let mediaDescriptions = store.getComposeData(realm, 'mediaDescriptions') || []
- if (mediaDescriptions[index] === rawText) {
+ let { realm, index, media } = this.get()
+ if (media[index].description === rawText) {
return
}
- while (mediaDescriptions.length <= index) {
- mediaDescriptions.push(null)
- }
- mediaDescriptions[index] = rawText
- store.setComposeData(realm, {mediaDescriptions})
+ media[index].description = rawText
+ store.setComposeData(realm, {media})
saveStore()
}, {init: false})
},
diff --git a/tests/spec/012-compose.js b/tests/spec/012-compose.js
index a06516c0..8b5edd0f 100644
--- a/tests/spec/012-compose.js
+++ b/tests/spec/012-compose.js
@@ -1,8 +1,9 @@
import { Selector as $ } from 'testcafe'
import {
- composeButton, composeInput, composeLengthIndicator, emojiButton, getComposeSelectionStart, getUrl,
+ composeButton, composeInput, composeLengthIndicator, emojiButton, getComposeSelectionStart,
+ getNthStatusContent, getUrl,
homeNavButton,
- notificationsNavButton,
+ notificationsNavButton, sleep,
times
} from '../utils'
import { loginAsFoobar } from '../roles'
@@ -97,3 +98,13 @@ test('inserts emoji without typing anything', async t => {
.click($('button img[title=":blobpeek:"]'))
.expect(composeInput.value).eql(':blobpeek: :blobpats: ')
})
+
+test('cannot post an empty status', async t => {
+ await loginAsFoobar(t)
+ await t
+ .expect(getNthStatusContent(0).innerText).contains('pinned toot 1')
+ .click(composeButton)
+ await sleep(2)
+ await t
+ .expect(getNthStatusContent(0).innerText).contains('pinned toot 1')
+})
diff --git a/tests/spec/013-compose-media.js b/tests/spec/013-compose-media.js
index 9f53fde4..fc9cd8d2 100644
--- a/tests/spec/013-compose-media.js
+++ b/tests/spec/013-compose-media.js
@@ -1,5 +1,6 @@
import {
- composeInput, getNthDeleteMediaButton, getNthMedia, mediaButton,
+ composeInput, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, homeNavButton, mediaButton,
+ settingsNavButton, sleep,
uploadKittenImage
} from '../utils'
import { loginAsFoobar } from '../roles'
@@ -51,16 +52,70 @@ test('removes media', async t => {
.expect(getNthMedia(2).exists).notOk()
})
-test('changes URLs as media is added/removed', async t => {
+test('does not add URLs as media is added/removed', async t => {
+ await loginAsFoobar(t)
+ await t
+ .typeText(composeInput, 'this is a toot')
+ .expect(mediaButton.exists).ok()
+ await (uploadKittenImage(1)())
+ await t.expect(composeInput.value).eql('this is a toot')
+ await (uploadKittenImage(1)())
+ await t.expect(composeInput.value).eql('this is a toot')
+ .click(getNthDeleteMediaButton(1))
+ .expect(composeInput.value).eql('this is a toot')
+ .click(getNthDeleteMediaButton(1))
+ .expect(composeInput.value).eql('this is a toot')
+})
+
+test('keeps media descriptions as media is removed', async t => {
await loginAsFoobar(t)
await t
.expect(mediaButton.exists).ok()
await (uploadKittenImage(1)())
- await t.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+$/)
- await (uploadKittenImage(1)())
- await t.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+ http:\/\/localhost:3000\/media\/\S+$/)
+ await t
+ .typeText(getNthMediaAltInput(1), 'kitten numero uno')
+ await (uploadKittenImage(2)())
+ await t
+ .typeText(getNthMediaAltInput(2), 'kitten numero dos')
+ .expect(getNthMediaAltInput(1).value).eql('kitten numero uno')
+ .expect(getNthMediaAltInput(2).value).eql('kitten numero dos')
+ .expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
+ .expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg')
.click(getNthDeleteMediaButton(1))
- .expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+$/)
- .click(getNthDeleteMediaButton(1))
- .expect(composeInput.value).eql('')
+ .expect(getNthMediaAltInput(1).value).eql('kitten numero dos')
+ .expect(getNthMedia(1).getAttribute('alt')).eql('kitten2.jpg')
+})
+
+test('keeps media in local storage', async t => {
+ await loginAsFoobar(t)
+ await t
+ .expect(mediaButton.exists).ok()
+ await (uploadKittenImage(1)())
+ await t
+ .typeText(getNthMediaAltInput(1), 'kitten numero uno')
+ await (uploadKittenImage(2)())
+ await t
+ .typeText(getNthMediaAltInput(2), 'kitten numero dos')
+ await t
+ .typeText(composeInput, 'hello hello')
+ .expect(composeInput.value).eql('hello hello')
+ .expect(getNthMediaAltInput(1).value).eql('kitten numero uno')
+ .expect(getNthMediaAltInput(2).value).eql('kitten numero dos')
+ .expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
+ .expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg')
+ await sleep(1)
+ await t
+ .click(settingsNavButton)
+ .click(homeNavButton)
+ .expect(composeInput.value).eql('hello hello')
+ .expect(getNthMediaAltInput(1).value).eql('kitten numero uno')
+ .expect(getNthMediaAltInput(2).value).eql('kitten numero dos')
+ .expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
+ .expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg')
+ .navigateTo('/')
+ .expect(composeInput.value).eql('hello hello')
+ .expect(getNthMediaAltInput(1).value).eql('kitten numero uno')
+ .expect(getNthMediaAltInput(2).value).eql('kitten numero dos')
+ .expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
+ .expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg')
})
diff --git a/tests/spec/109-compose-media.js b/tests/spec/109-compose-media.js
index 86fe5846..fd816819 100644
--- a/tests/spec/109-compose-media.js
+++ b/tests/spec/109-compose-media.js
@@ -1,5 +1,5 @@
import {
- composeButton, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, getNthStatusAndImage, getUrl,
+ composeButton, composeInput, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, getNthStatusAndImage, getUrl,
homeNavButton,
mediaButton, notificationsNavButton,
uploadKittenImage
@@ -77,3 +77,15 @@ test('saves alts to local storage', async t => {
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('kitten numero uno')
.expect(getNthStatusAndImage(0, 1).getAttribute('alt')).eql('kitten numero dos')
})
+
+test('can post a status with empty content if there is media', async t => {
+ await loginAsFoobar(t)
+ await t
+ .expect(mediaButton.hasAttribute('disabled')).notOk()
+ .typeText(composeInput, 'this is a toot')
+ await (uploadKittenImage(1)())
+ await t
+ .typeText(getNthMediaAltInput(1), 'just an image!')
+ await t.click(composeButton)
+ .expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('just an image!')
+})