parent
							
								
									d49af06fbd
								
							
						
					
					
						commit
						b60d636ee2
					
				
					 9 changed files with 112 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@
 | 
			
		|||
  <ComposeLengthGauge {length} {overLimit} />
 | 
			
		||||
  <ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} />
 | 
			
		||||
  <ComposeLengthIndicator {length} {overLimit} />
 | 
			
		||||
  <ComposeMedia {realm} {media} {mediaDescriptions} />
 | 
			
		||||
  <ComposeMedia {realm} {media} />
 | 
			
		||||
</div>
 | 
			
		||||
<div class="compose-box-button-sentinel {hideAndFadeIn}" ref:sentinel></div>
 | 
			
		||||
<div class="compose-box-button-wrapper {realm === 'home' ? 'compose-button-sticky' : ''} {hideAndFadeIn}" >
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{#if media.length}
 | 
			
		||||
  <div class="compose-media-container" style="grid-template-columns: repeat({media.length}, 1fr);">
 | 
			
		||||
    {#each media as mediaItem, index}
 | 
			
		||||
      <ComposeMediaItem {realm} {mediaItem} {index} {mediaDescriptions} />
 | 
			
		||||
      <ComposeMediaItem {realm} {mediaItem} {index} {media} />
 | 
			
		||||
    {/each}
 | 
			
		||||
  </div>
 | 
			
		||||
{/if}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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})
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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')
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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!')
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue