diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index 3ea96a7a..a80c5c2e 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -13,7 +13,9 @@ import {
   faPlayCircle,
   faTimes,
   faStop,
-  faSearchPlus
+  faSearchPlus,
+  faTrashAlt,
+  faPencilAlt
 } from '@fortawesome/free-solid-svg-icons'
 
 library.add(
@@ -24,19 +26,25 @@ library.add(
   faPlayCircle,
   faTimes,
   faStop,
-  faSearchPlus
+  faSearchPlus,
+  faTrashAlt,
+  faPencilAlt
 )
 
 const Attachment = {
   props: [
     'attachment',
+    'description',
+    'hideDescription',
     'nsfw',
     'size',
     'setMedia',
-    'naturalSizeLoad'
+    'remove',
+    'edit'
   ],
   data () {
     return {
+      localDescription: this.description || this.attachment.description,
       nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,
       hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,
       preloadImage: this.$store.getters.mergedConfig.preloadImage,
@@ -93,9 +101,6 @@ const Attachment = {
     isEmpty () {
       return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown'
     },
-    isSmall () {
-      return this.size === 'small'
-    },
     useModal () {
       const modalTypes = this.size === 'hide' ? ['image', 'video', 'audio']
         : this.mergedConfig.playVideosInModal
@@ -105,6 +110,11 @@ const Attachment = {
     },
     ...mapGetters(['mergedConfig'])
   },
+  watch: {
+    localDescription (newVal) {
+      this.onEdit(newVal)
+    }
+  },
   methods: {
     linkClicked ({ target }) {
       if (target.tagName === 'A') {
@@ -121,6 +131,12 @@ const Attachment = {
       this.$emit('setMedia')
       this.$store.dispatch('setCurrentMedia', this.attachment)
     },
+    onEdit (event) {
+      console.log('ONEDIT', event)
+      this.edit && this.edit(this.attachment, event)
+    },
+    onRemove () {
+      this.remove && this.remove(this.attachment)
     },
     stopFlash () {
       this.$refs.flash.closePlayer()
@@ -154,7 +170,7 @@ const Attachment = {
     onImageLoad (image) {
       const width = image.naturalWidth
       const height = image.naturalHeight
-      this.naturalSizeLoad && this.naturalSizeLoad({ width, height })
+      this.$emit('naturalSizeLoad', { id: this.attachment.id, width, height })
     }
   }
 }
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index fe9e9398..9b1e83a7 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -12,151 +12,180 @@
       :href="attachment.url"
       :alt="attachment.description"
       :title="attachment.description"
+      @click.prevent=""
     >
       <FAIcon :icon="placeholderIconClass" />
       <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }}
     </a>
   </button>
   <div
-    v-else
-    v-show="!isEmpty"
     class="Attachment"
     :class="classNames"
+    v-else
   >
-    <a
-      v-if="hidden"
-      class="image-container"
-      :href="attachment.url"
-      :alt="attachment.description"
-      :title="attachment.description"
-      @click.prevent.stop="toggleHidden"
-    >
-      <img
-        :key="nsfwImage"
-        class="nsfw"
-        :src="nsfwImage"
-      >
-      <FAIcon
-        v-if="type === 'video'"
-        class="play-icon"
-        icon="play-circle"
-      />
-    </a>
     <div
-      class="attachment-buttons"
-      v-if="!hidden"
-      >
-      <button
-        v-if="type === 'flash' && flashLoaded"
-        class="button-unstyled attachment-button"
-        @click.prevent="stopFlash"
-      >
-        <FAIcon icon="stop" />
-      </button>
-      <button
-        v-if="!useModal"
-        class="button-unstyled attachment-button"
-        @click.prevent="openModalForce"
-      >
-        <FAIcon icon="search-plus" />
-      </button>
-      <button
-        v-if="nsfw && hideNsfwLocal"
-        class="button-unstyled attachment-button"
-        @click.prevent="toggleHidden"
-      >
-        <FAIcon icon="times" />
-      </button>
-    </div>
-
-    <a
-      v-if="type === 'image' && (!hidden || preloadImage)"
-      class="image-container"
-      :class="{'-hidden': hidden && preloadImage }"
-      :href="attachment.url"
-      target="_blank"
-      @click.stop.prevent="openModal"
+      class="attachment-wrapper"
+      v-show="!isEmpty"
     >
-      <StillImage
-        class="image"
-        :referrerpolicy="referrerpolicy"
-        :mimetype="attachment.mimetype"
-        :src="attachment.large_thumb_url || attachment.url"
-        :image-load-handler="onImageLoad"
-        :alt="attachment.description"
-      />
-    </a>
-
-    <a
-      v-if="type === 'video' && !hidden"
-      class="video-container"
-      :href="attachment.url"
-      @click.stop.prevent="openModal"
-    >
-      <VideoAttachment
-        class="video"
-        :attachment="attachment"
-        :controls="!useModal"
-        @play="$emit('play')"
-        @pause="$emit('pause')"
-      />
-      <FAIcon
-        v-if="useModal"
-        class="play-icon"
-        icon="play-circle"
-      />
-    </a>
-
-    <a
-      v-if="type === 'audio' && !hidden"
-      class="audio-container"
-      :href="attachment.url"
-      @click.stop.prevent="openModal"
-    >
-      <audio
-        v-if="type === 'audio'"
-        :src="attachment.url"
+      <a
+        v-if="hidden"
+        class="image-container"
+        :href="attachment.url"
         :alt="attachment.description"
         :title="attachment.description"
-        controls
-        @play="$emit('play')"
-        @pause="$emit('pause')"
-      />
-    </a>
-
-    <div
-      v-if="type === 'html' && attachment.oembed"
-      class="oembed-container"
-      @click.prevent="linkClicked"
-    >
-      <div
-        v-if="attachment.thumb_url"
-        class="image"
+        @click.prevent.stop="toggleHidden"
       >
-        <img :src="attachment.thumb_url">
+        <img
+          :key="nsfwImage"
+          class="nsfw"
+          :src="nsfwImage"
+        >
+        <FAIcon
+          v-if="type === 'video'"
+          class="play-icon"
+          icon="play-circle"
+        />
+      </a>
+      <div
+        class="attachment-buttons"
+        v-if="!hidden"
+        >
+        <button
+          v-if="type === 'flash' && flashLoaded"
+          class="button-unstyled attachment-button"
+          @click.prevent="stopFlash"
+        >
+          <FAIcon icon="stop" />
+        </button>
+        <button
+          v-if="!useModal"
+          class="button-unstyled attachment-button"
+          @click.prevent="openModalForce"
+        >
+          <FAIcon icon="search-plus" />
+        </button>
+        <button
+          v-if="nsfw && hideNsfwLocal"
+          class="button-unstyled attachment-button"
+          @click.prevent="toggleHidden"
+        >
+          <FAIcon icon="times" />
+        </button>
+        <button
+          v-if="remove"
+          class="button-unstyled attachment-button"
+          @click.prevent="onRemove"
+        >
+          <FAIcon icon="trash-alt" />
+        </button>
       </div>
-      <div class="text">
-        <!-- eslint-disable vue/no-v-html -->
-        <h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
-        <div v-html="attachment.oembed.oembedHTML" />
-        <!-- eslint-enable vue/no-v-html -->
-      </div>
-    </div>
 
-    <a
-      v-if="type === 'flash' && !hidden"
-      class="flash-container"
-      :href="attachment.url"
-      @click.stop.prevent="openModal"
+      <a
+        v-if="type === 'image' && (!hidden || preloadImage)"
+        class="image-container"
+        :class="{'-hidden': hidden && preloadImage }"
+        :href="attachment.url"
+        target="_blank"
+        @click.stop.prevent="openModal"
+      >
+        <StillImage
+          class="image"
+          :referrerpolicy="referrerpolicy"
+          :mimetype="attachment.mimetype"
+          :src="attachment.large_thumb_url || attachment.url"
+          :image-load-handler="onImageLoad"
+          :alt="attachment.description"
+        />
+      </a>
+
+      <a
+        v-if="type === 'video' && !hidden"
+        class="video-container"
+        :href="attachment.url"
+        @click.stop.prevent="openModal"
+      >
+        <VideoAttachment
+          class="video"
+          :attachment="attachment"
+          :controls="!useModal"
+          @play="$emit('play')"
+          @pause="$emit('pause')"
+        />
+        <FAIcon
+          v-if="useModal"
+          class="play-icon"
+          icon="play-circle"
+        />
+      </a>
+
+      <a
+        v-if="type === 'audio' && !hidden"
+        class="audio-container"
+        :href="attachment.url"
+        @click.stop.prevent="openModal"
+      >
+        <audio
+          v-if="type === 'audio'"
+          :src="attachment.url"
+          :alt="attachment.description"
+          :title="attachment.description"
+          controls
+          @play="$emit('play')"
+          @pause="$emit('pause')"
+        />
+      </a>
+
+      <div
+        v-if="type === 'html' && attachment.oembed"
+        class="oembed-container"
+        @click.prevent="linkClicked"
+      >
+        <div
+          v-if="attachment.thumb_url"
+          class="image"
+        >
+          <img :src="attachment.thumb_url">
+        </div>
+        <div class="text">
+          <!-- eslint-disable vue/no-v-html -->
+          <h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
+          <div v-html="attachment.oembed.oembedHTML" />
+          <!-- eslint-enable vue/no-v-html -->
+        </div>
+      </div>
+
+      <a
+        v-if="type === 'flash' && !hidden"
+        class="flash-container"
+        :href="attachment.url"
+        @click.stop.prevent="openModal"
+      >
+        <Flash
+          class="flash"
+          ref="flash"
+          :src="attachment.large_thumb_url || attachment.url"
+          @playerOpened="setFlashLoaded(true)"
+          @playerClosed="setFlashLoaded(false)"
+        />
+      </a>
+    </div>
+    <div
+      v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
+      class="description-container"
+      :class="{ '-static': !edit }"
     >
-      <Flash
-        class="flash"
-        ref="flash"
-        :src="attachment.large_thumb_url || attachment.url"
-        @playerOpened="setFlashLoaded(true)"
-        @playerClosed="setFlashLoaded(false)"
-      />
-    </a>
+      <input
+        v-if="edit"
+        v-model="localDescription"
+        type="text"
+        class="description-field"
+        :placeholder="$t('post_status.media_description')"
+        @keydown.enter.prevent=""
+      >
+      <p v-else>
+        {{ localDescription }}
+      </p>
+    </div>
   </div>
 </template>
 
diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js
index 134ea77e..cca67dbd 100644
--- a/src/components/gallery/gallery.js
+++ b/src/components/gallery/gallery.js
@@ -4,9 +4,15 @@ import { sumBy } from 'lodash'
 const Gallery = {
   props: [
     'attachments',
+    'limitRows',
+    'descriptions',
     'nsfw',
     'setMedia',
-    'size'
+    'size',
+    'editable',
+    'removeAttachment',
+    'editAttachment',
+    'maxPerRow'
   ],
   data () {
     return {
@@ -20,24 +26,19 @@ const Gallery = {
       if (!this.attachments) {
         return []
       }
-      console.log(this.size)
       if (this.size === 'hide') {
         return this.attachments.map(item => ({ minimal: true, items: [item] }))
       }
       const rows = this.attachments.reduce((acc, attachment, i) => {
+        if (this.size === 'small' && acc.length === 2) return acc
         if (attachment.mimetype.includes('audio')) {
           return [...acc, { audio: true, items: [attachment] }, { items: [] }]
         }
-        const maxPerRow = 3
-        const attachmentsRemaining = this.attachments.length - i - 1
+        const maxPerRow = this.maxPerRow || 3
+        const attachmentsRemaining = this.attachments.length - i + 1
         const currentRow = acc[acc.length - 1].items
-        if (
-          currentRow.length <= maxPerRow ||
-            attachmentsRemaining === 1
-        ) {
-          currentRow.push(attachment)
-        }
-        if (currentRow.length === maxPerRow && attachmentsRemaining > 1) {
+        currentRow.push(attachment)
+        if (currentRow.length >= maxPerRow && attachmentsRemaining > maxPerRow) {
           return [...acc, { items: [] }]
         } else {
           return acc
@@ -51,7 +52,9 @@ const Gallery = {
       }, 0)
     },
     tooManyAttachments () {
-      if (this.size === 'hide') {
+      if (this.editable || this.size === 'small') {
+        return false
+      } else if (this.size === 'hide') {
         return this.attachments.length > 8
       } else {
         return this.attachmentsDimensionalScore > 1
@@ -59,8 +62,8 @@ const Gallery = {
     }
   },
   methods: {
-    onNaturalSizeLoad (id, size) {
-      this.$set(this.sizes, id, size)
+    onNaturalSizeLoad ({ id, width, height }) {
+      this.$set(this.sizes, id, { width, height })
     },
     rowStyle (row) {
       if (row.audio) {
@@ -81,8 +84,11 @@ const Gallery = {
       this.hidingLong = event
     },
     openGallery () {
-      this.setMedia()
-      this.$store.dispatch('setCurrent', this.attachments[0])
+      this.$store.dispatch('setMedia', this.attachments)
+      this.$store.dispatch('setCurrentMedia', this.attachments[0])
+    },
+    onMedia () {
+      this.$store.dispatch('setMedia', this.attachments)
     }
   }
 }
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 8e08e514..cedf64d3 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -17,13 +17,18 @@
             v-for="attachment in row.items"
             class="gallery-item"
             :key="attachment.id"
-            :set-media="setMedia"
             :nsfw="nsfw"
             :attachment="attachment"
             :allow-play="false"
             :size="size"
-            :natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)"
+            :editable="editable"
+            :remove="removeAttachment"
+            :edit="editAttachment"
+            :description="descriptions && descriptions[attachment.id]"
+            :hideDescription="tooManyAttachments && hidingLong"
             :style="itemStyle(attachment.id, row.items)"
+            @setMedia="onMedia"
+            @naturalSizeLoad="onNaturalSizeLoad"
           />
         </div>
       </div>
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 5342894f..1a12db9c 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -4,6 +4,7 @@ import ScopeSelector from '../scope_selector/scope_selector.vue'
 import EmojiInput from '../emoji_input/emoji_input.vue'
 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 fileTypeService from '../../services/file_type/file_type.service.js'
 import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
@@ -85,7 +86,8 @@ const PostStatusForm = {
     Checkbox,
     Select,
     Attachment,
-    StatusContent
+    StatusContent,
+    Gallery
   },
   mounted () {
     this.updateIdempotencyKey()
@@ -388,6 +390,10 @@ const PostStatusForm = {
       this.newStatus.files.splice(index, 1)
       this.$emit('resize')
     },
+    editAttachment (fileInfo, newText) {
+      console.log(fileInfo, newText)
+      this.newStatus.mediaDescriptions[fileInfo.id] = newText
+    },
     uploadFailed (errString, templateArgs) {
       templateArgs = templateArgs || {}
       this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs)
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index fbda41d6..c6f84a4b 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -287,32 +287,21 @@
           @click="clearError"
         />
       </div>
-      <div class="attachments">
-        <div
-          v-for="file in newStatus.files"
-          :key="file.url"
-          class="media-upload-wrapper"
-        >
-          <button
-            class="button-unstyled hider"
-            @click="removeMediaFile(file)"
-          >
-            <FAIcon icon="times" />
-          </button>
-          <attachment
-            :attachment="file"
-            :set-media="() => $store.dispatch('setMedia', newStatus.files)"
-            size="small"
-            allow-play="false"
-          />
-          <input
-            v-model="newStatus.mediaDescriptions[file.id]"
-            type="text"
-            :placeholder="$t('post_status.media_description')"
-            @keydown.enter.prevent=""
-          >
-        </div>
-      </div>
+      <gallery
+        v-if="newStatus.files && newStatus.files.length > 0"
+        class="attachments"
+        :maxPerRow="1"
+        :nsfw="false"
+        :attachments="newStatus.files"
+        :descriptions="newStatus.mediaDescriptions"
+        :set-media="() => $store.dispatch('setMedia', newStatus.files)"
+        :editable="true"
+        :editAttachment="editAttachment"
+        :removeAttachment="removeMediaFile"
+        size="small"
+        @play="$emit('mediaplay', attachment.id)"
+        @pause="$emit('mediapause', attachment.id)"
+      />
       <div
         v-if="newStatus.files.length > 0 && !disableSensitivityCheckbox"
         class="upload_settings"
@@ -507,15 +496,6 @@
     flex-direction: column;
   }
 
-   .attachments .media-upload-wrapper {
-    position: relative;
-
-    .attachment {
-      margin: 0;
-      padding: 0;
-    }
-  }
-
   .btn {
     cursor: pointer;
   }
@@ -616,11 +596,4 @@
     border: 2px dashed var(--text, $fallback--text);
   }
 }
-
-// todo: unify with attachment.vue (otherwise the uploaded images are not minified unless a status with an attachment was displayed before)
-img.media-upload, .media-upload-container > video {
-  line-height: 0;
-  max-height: 200px;
-  max-width: 100%;
-}
 </style>