parent
d49af06fbd
commit
b60d636ee2
|
@ -22,7 +22,7 @@ export async function insertHandleForReply (statusId) {
|
||||||
|
|
||||||
export async function postStatus (realm, text, inReplyToId, mediaIds,
|
export async function postStatus (realm, text, inReplyToId, mediaIds,
|
||||||
sensitive, spoilerText, visibility,
|
sensitive, spoilerText, visibility,
|
||||||
mediaDescriptions = [], inReplyToUuid) {
|
mediaDescriptions, inReplyToUuid) {
|
||||||
let { currentInstance, accessToken, online } = store.get()
|
let { currentInstance, accessToken, online } = store.get()
|
||||||
|
|
||||||
if (!online) {
|
if (!online) {
|
||||||
|
@ -30,6 +30,9 @@ export async function postStatus (realm, text, inReplyToId, mediaIds,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
text = text || ''
|
||||||
|
mediaDescriptions = mediaDescriptions || []
|
||||||
|
|
||||||
store.set({
|
store.set({
|
||||||
postingStatus: true
|
postingStatus: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,13 +11,11 @@ export async function doMediaUpload (realm, file) {
|
||||||
let composeMedia = store.getComposeData(realm, 'media') || []
|
let composeMedia = store.getComposeData(realm, 'media') || []
|
||||||
composeMedia.push({
|
composeMedia.push({
|
||||||
data: response,
|
data: response,
|
||||||
file: { name: file.name }
|
file: { name: file.name },
|
||||||
|
description: ''
|
||||||
})
|
})
|
||||||
let composeText = store.getComposeData(realm, 'text') || ''
|
|
||||||
composeText += ' ' + response.text_url
|
|
||||||
store.setComposeData(realm, {
|
store.setComposeData(realm, {
|
||||||
media: composeMedia,
|
media: composeMedia
|
||||||
text: composeText
|
|
||||||
})
|
})
|
||||||
scheduleIdleTask(() => store.save())
|
scheduleIdleTask(() => store.save())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -30,20 +28,10 @@ export async function doMediaUpload (realm, file) {
|
||||||
|
|
||||||
export function deleteMedia (realm, i) {
|
export function deleteMedia (realm, i) {
|
||||||
let composeMedia = store.getComposeData(realm, 'media')
|
let composeMedia = store.getComposeData(realm, 'media')
|
||||||
let deletedMedia = composeMedia.splice(i, 1)[0]
|
composeMedia.splice(i, 1)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
store.setComposeData(realm, {
|
store.setComposeData(realm, {
|
||||||
media: composeMedia,
|
media: composeMedia
|
||||||
text: composeText,
|
|
||||||
mediaDescriptions: mediaDescriptions
|
|
||||||
})
|
})
|
||||||
scheduleIdleTask(() => store.save())
|
scheduleIdleTask(() => store.save())
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ export async function postStatus (instanceName, accessToken, text, inReplyToId,
|
||||||
|
|
||||||
for (let key of Object.keys(body)) {
|
for (let key of Object.keys(body)) {
|
||||||
let value = body[key]
|
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]
|
delete body[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<ComposeLengthGauge {length} {overLimit} />
|
<ComposeLengthGauge {length} {overLimit} />
|
||||||
<ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} />
|
<ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} />
|
||||||
<ComposeLengthIndicator {length} {overLimit} />
|
<ComposeLengthIndicator {length} {overLimit} />
|
||||||
<ComposeMedia {realm} {media} {mediaDescriptions} />
|
<ComposeMedia {realm} {media} />
|
||||||
</div>
|
</div>
|
||||||
<div class="compose-box-button-sentinel {hideAndFadeIn}" ref:sentinel></div>
|
<div class="compose-box-button-sentinel {hideAndFadeIn}" ref:sentinel></div>
|
||||||
<div class="compose-box-button-wrapper {realm === 'home' ? 'compose-button-sticky' : ''} {hideAndFadeIn}" >
|
<div class="compose-box-button-wrapper {realm === 'home' ? 'compose-button-sticky' : ''} {hideAndFadeIn}" >
|
||||||
|
@ -186,8 +186,7 @@
|
||||||
overLimit: ({ length, $maxStatusChars }) => length > $maxStatusChars,
|
overLimit: ({ length, $maxStatusChars }) => length > $maxStatusChars,
|
||||||
contentWarningShown: ({ composeData }) => composeData.contentWarningShown,
|
contentWarningShown: ({ composeData }) => composeData.contentWarningShown,
|
||||||
contentWarning: ({ composeData }) => composeData.contentWarning || '',
|
contentWarning: ({ composeData }) => composeData.contentWarning || '',
|
||||||
timelineInitialized: ({ $timelineInitialized }) => $timelineInitialized,
|
timelineInitialized: ({ $timelineInitialized }) => $timelineInitialized
|
||||||
mediaDescriptions: ({ composeData }) => composeData.mediaDescriptions || []
|
|
||||||
},
|
},
|
||||||
transitions: {
|
transitions: {
|
||||||
slide
|
slide
|
||||||
|
@ -214,14 +213,14 @@
|
||||||
contentWarning,
|
contentWarning,
|
||||||
realm,
|
realm,
|
||||||
overLimit,
|
overLimit,
|
||||||
mediaDescriptions,
|
|
||||||
inReplyToUuid
|
inReplyToUuid
|
||||||
} = this.get()
|
} = this.get()
|
||||||
let sensitive = media.length && !!contentWarning
|
let sensitive = media.length && !!contentWarning
|
||||||
let mediaIds = media.map(_ => _.data.id)
|
let mediaIds = media.map(_ => _.data.id)
|
||||||
|
let mediaDescriptions = media.map(_ => _.description)
|
||||||
let inReplyTo = (realm === 'home' || realm === 'dialog') ? null : realm
|
let inReplyTo = (realm === 'home' || realm === 'dialog') ? null : realm
|
||||||
|
|
||||||
if (!text || overLimit) {
|
if (overLimit || (!text && !media.length)) {
|
||||||
return // do nothing if invalid
|
return // do nothing if invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{#if media.length}
|
{#if media.length}
|
||||||
<div class="compose-media-container" style="grid-template-columns: repeat({media.length}, 1fr);">
|
<div class="compose-media-container" style="grid-template-columns: repeat({media.length}, 1fr);">
|
||||||
{#each media as mediaItem, index}
|
{#each media as mediaItem, index}
|
||||||
<ComposeMediaItem {realm} {mediaItem} {index} {mediaDescriptions} />
|
<ComposeMediaItem {realm} {mediaItem} {index} {media} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -95,10 +95,10 @@
|
||||||
methods: {
|
methods: {
|
||||||
observe,
|
observe,
|
||||||
setupSyncFromStore () {
|
setupSyncFromStore () {
|
||||||
this.observe('mediaDescriptions', mediaDescriptions => {
|
this.observe('media', media => {
|
||||||
mediaDescriptions = mediaDescriptions || []
|
media = media || []
|
||||||
let { index, rawText } = this.get()
|
let { index, rawText } = this.get()
|
||||||
let text = mediaDescriptions[index] || ''
|
let text = (media[index] && media[index].description) || ''
|
||||||
if (rawText !== text) {
|
if (rawText !== text) {
|
||||||
this.set({rawText: text})
|
this.set({rawText: text})
|
||||||
}
|
}
|
||||||
|
@ -108,17 +108,12 @@
|
||||||
const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
|
const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
|
||||||
|
|
||||||
this.observe('rawText', rawText => {
|
this.observe('rawText', rawText => {
|
||||||
let { realm } = this.get()
|
let { realm, index, media } = this.get()
|
||||||
let { index } = this.get()
|
if (media[index].description === rawText) {
|
||||||
let mediaDescriptions = store.getComposeData(realm, 'mediaDescriptions') || []
|
|
||||||
if (mediaDescriptions[index] === rawText) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
while (mediaDescriptions.length <= index) {
|
media[index].description = rawText
|
||||||
mediaDescriptions.push(null)
|
store.setComposeData(realm, {media})
|
||||||
}
|
|
||||||
mediaDescriptions[index] = rawText
|
|
||||||
store.setComposeData(realm, {mediaDescriptions})
|
|
||||||
saveStore()
|
saveStore()
|
||||||
}, {init: false})
|
}, {init: false})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Selector as $ } from 'testcafe'
|
import { Selector as $ } from 'testcafe'
|
||||||
import {
|
import {
|
||||||
composeButton, composeInput, composeLengthIndicator, emojiButton, getComposeSelectionStart, getUrl,
|
composeButton, composeInput, composeLengthIndicator, emojiButton, getComposeSelectionStart,
|
||||||
|
getNthStatusContent, getUrl,
|
||||||
homeNavButton,
|
homeNavButton,
|
||||||
notificationsNavButton,
|
notificationsNavButton, sleep,
|
||||||
times
|
times
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { loginAsFoobar } from '../roles'
|
import { loginAsFoobar } from '../roles'
|
||||||
|
@ -97,3 +98,13 @@ test('inserts emoji without typing anything', async t => {
|
||||||
.click($('button img[title=":blobpeek:"]'))
|
.click($('button img[title=":blobpeek:"]'))
|
||||||
.expect(composeInput.value).eql(':blobpeek: :blobpats: ')
|
.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')
|
||||||
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
composeInput, getNthDeleteMediaButton, getNthMedia, mediaButton,
|
composeInput, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, homeNavButton, mediaButton,
|
||||||
|
settingsNavButton, sleep,
|
||||||
uploadKittenImage
|
uploadKittenImage
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { loginAsFoobar } from '../roles'
|
import { loginAsFoobar } from '../roles'
|
||||||
|
@ -51,16 +52,70 @@ test('removes media', async t => {
|
||||||
.expect(getNthMedia(2).exists).notOk()
|
.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 loginAsFoobar(t)
|
||||||
await t
|
await t
|
||||||
.expect(mediaButton.exists).ok()
|
.expect(mediaButton.exists).ok()
|
||||||
await (uploadKittenImage(1)())
|
await (uploadKittenImage(1)())
|
||||||
await t.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+$/)
|
await t
|
||||||
await (uploadKittenImage(1)())
|
.typeText(getNthMediaAltInput(1), 'kitten numero uno')
|
||||||
await t.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+ http:\/\/localhost:3000\/media\/\S+$/)
|
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))
|
.click(getNthDeleteMediaButton(1))
|
||||||
.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+$/)
|
.expect(getNthMediaAltInput(1).value).eql('kitten numero dos')
|
||||||
.click(getNthDeleteMediaButton(1))
|
.expect(getNthMedia(1).getAttribute('alt')).eql('kitten2.jpg')
|
||||||
.expect(composeInput.value).eql('')
|
})
|
||||||
|
|
||||||
|
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')
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
composeButton, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, getNthStatusAndImage, getUrl,
|
composeButton, composeInput, getNthDeleteMediaButton, getNthMedia, getNthMediaAltInput, getNthStatusAndImage, getUrl,
|
||||||
homeNavButton,
|
homeNavButton,
|
||||||
mediaButton, notificationsNavButton,
|
mediaButton, notificationsNavButton,
|
||||||
uploadKittenImage
|
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, 0).getAttribute('alt')).eql('kitten numero uno')
|
||||||
.expect(getNthStatusAndImage(0, 1).getAttribute('alt')).eql('kitten numero dos')
|
.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!')
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in a new issue