From e654fead23ebb457f81e8642c65e1f3e98ee0027 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 17 Jun 2021 16:29:46 +0300
Subject: refactored attachments and gallery. All attachments now are in
 gallery.

---
 src/components/attachment/attachment.vue | 307 ++++++++-----------------------
 1 file changed, 72 insertions(+), 235 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index f80badfd..fe9e9398 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -1,7 +1,8 @@
 <template>
-  <div
+  <button
     v-if="usePlaceholder"
-    :class="{ 'fullwidth': fullwidth }"
+    class="Attachment -placeholder button-unstyled"
+    :class="classNames"
     @click="openModal"
   >
     <a
@@ -15,16 +16,16 @@
       <FAIcon :icon="placeholderIconClass" />
       <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }}
     </a>
-  </div>
+  </button>
   <div
     v-else
     v-show="!isEmpty"
-    class="attachment"
-    :class="{[type]: true, loading, 'fullwidth': fullwidth, 'nsfw-placeholder': hidden}"
+    class="Attachment"
+    :class="classNames"
   >
     <a
       v-if="hidden"
-      class="image-attachment"
+      class="image-container"
       :href="attachment.url"
       :alt="attachment.description"
       :title="attachment.description"
@@ -34,7 +35,6 @@
         :key="nsfwImage"
         class="nsfw"
         :src="nsfwImage"
-        :class="{'small': isSmall}"
       >
       <FAIcon
         v-if="type === 'video'"
@@ -42,21 +42,40 @@
         icon="play-circle"
       />
     </a>
-    <button
-      v-if="nsfw && hideNsfwLocal && !hidden"
-      class="button-unstyled hider"
-      @click.prevent="toggleHidden"
-    >
-      <FAIcon icon="times" />
-    </button>
+    <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-attachment"
-      :class="{'hidden': hidden && preloadImage }"
+      class="image-container"
+      :class="{'-hidden': hidden && preloadImage }"
       :href="attachment.url"
       target="_blank"
-      @click="openModal"
+      @click.stop.prevent="openModal"
     >
       <StillImage
         class="image"
@@ -71,37 +90,43 @@
     <a
       v-if="type === 'video' && !hidden"
       class="video-container"
-      :class="{'small': isSmall}"
-      :href="allowPlay ? undefined : attachment.url"
-      @click="openModal"
+      :href="attachment.url"
+      @click.stop.prevent="openModal"
     >
       <VideoAttachment
         class="video"
         :attachment="attachment"
-        :controls="allowPlay"
+        :controls="!useModal"
         @play="$emit('play')"
         @pause="$emit('pause')"
       />
       <FAIcon
-        v-if="!allowPlay"
+        v-if="useModal"
         class="play-icon"
         icon="play-circle"
       />
     </a>
 
-    <audio
-      v-if="type === 'audio'"
-      :src="attachment.url"
-      :alt="attachment.description"
-      :title="attachment.description"
-      controls
-      @play="$emit('play')"
-      @pause="$emit('pause')"
-    />
+    <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"
+      class="oembed-container"
       @click.prevent="linkClicked"
     >
       <div
@@ -118,211 +143,23 @@
       </div>
     </div>
 
-    <Flash
-      v-if="type === 'flash'"
-      :src="attachment.large_thumb_url || attachment.url"
-    />
+    <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>
 </template>
 
 <script src="./attachment.js"></script>
 
-<style lang="scss">
-@import '../../_variables.scss';
-
-.attachments {
-  display: flex;
-  flex-wrap: wrap;
-
-  .non-gallery {
-    max-width: 100%;
-  }
-
-  .placeholder {
-    display: inline-block;
-    padding: 0.3em 1em 0.3em 0;
-    color: $fallback--link;
-    color: var(--postLink, $fallback--link);
-    overflow: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-    max-width: 100%;
-
-    svg {
-      color: inherit;
-    }
-  }
-
-  .nsfw-placeholder {
-    cursor: pointer;
-
-    &.loading {
-      cursor: progress;
-    }
-  }
-
-  .attachment {
-    position: relative;
-    margin-top: 0.5em;
-    align-self: flex-start;
-    line-height: 0;
-
-    border-style: solid;
-    border-width: 1px;
-    border-radius: $fallback--attachmentRadius;
-    border-radius: var(--attachmentRadius, $fallback--attachmentRadius);
-    border-color: $fallback--border;
-    border-color: var(--border, $fallback--border);
-    overflow: hidden;
-  }
-
-  .non-gallery.attachment {
-    &.flash,
-    &.video {
-      flex: 1 0 40%;
-    }
-    .nsfw {
-      height: 260px;
-    }
-    .small {
-      height: 120px;
-      flex-grow: 0;
-    }
-    .video {
-      height: 260px;
-      display: flex;
-    }
-    video {
-      max-height: 100%;
-      object-fit: contain;
-    }
-  }
-
-  .fullwidth {
-    flex-basis: 100%;
-  }
-  // fixes small gap below video
-  &.video {
-    line-height: 0;
-  }
-
-  .video-container {
-    display: flex;
-    max-height: 100%;
-  }
-
-  .video {
-    width: 100%;
-    height: 100%;
-  }
-
-  .play-icon {
-    position: absolute;
-    font-size: 64px;
-    top: calc(50% - 32px);
-    left: calc(50% - 32px);
-    color: rgba(255, 255, 255, 0.75);
-    text-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
-  }
-
-  .play-icon::before {
-    margin: 0;
-  }
-
-  &.html {
-    flex-basis: 90%;
-    width: 100%;
-    display: flex;
-  }
-
-  .hider {
-    position: absolute;
-    right: 0;
-    margin: 10px;
-    padding: 0;
-    z-index: 4;
-    border-radius: $fallback--tooltipRadius;
-    border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
-    text-align: center;
-    width: 2em;
-    height: 2em;
-    font-size: 1.25em;
-    // TODO: theming? hard to theme with unknown background image color
-    background: rgba(230, 230, 230, 0.7);
-    .svg-inline--fa {
-      color: rgba(0, 0, 0, 0.6);
-    }
-    &:hover .svg-inline--fa {
-      color: rgba(0, 0, 0, 0.9);
-    }
-  }
-
-  video {
-    z-index: 0;
-  }
-
-  audio {
-    width: 100%;
-  }
-
-  img.media-upload {
-    line-height: 0;
-    max-height: 200px;
-    max-width: 100%;
-  }
-
-  .oembed {
-    line-height: 1.2em;
-    flex: 1 0 100%;
-    width: 100%;
-    margin-right: 15px;
-    display: flex;
-
-    img {
-      width: 100%;
-    }
-
-    .image {
-      flex: 1;
-      img {
-        border: 0px;
-        border-radius: 5px;
-        height: 100%;
-        object-fit: cover;
-      }
-    }
-
-    .text {
-      flex: 2;
-      margin: 8px;
-      word-break: break-all;
-      h1 {
-        font-size: 14px;
-        margin: 0px;
-      }
-    }
-  }
-
-  .image-attachment {
-    &,
-    & .image {
-      width: 100%;
-      height: 100%;
-    }
-
-    &.hidden {
-      display: none;
-    }
-
-    .nsfw {
-      object-fit: cover;
-      width: 100%;
-      height: 100%;
-    }
-
-    img {
-      image-orientation: from-image; // NOTE: only FF supports this
-    }
-  }
-}
-</style>
+<style src="./attachment.scss" lang="scss"></style>
-- 
cgit v1.2.3-70-g09d2


From f15599e6e57477131ba5592e2874916ed1da06e2 Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 18 Jun 2021 02:04:01 +0300
Subject: gallery in post status form!

---
 src/components/attachment/attachment.js            |  30 ++-
 src/components/attachment/attachment.vue           | 279 ++++++++++++---------
 src/components/gallery/gallery.js                  |  38 +--
 src/components/gallery/gallery.vue                 |   9 +-
 .../post_status_form/post_status_form.js           |   8 +-
 .../post_status_form/post_status_form.vue          |  57 ++---
 6 files changed, 228 insertions(+), 193 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

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"
+      <a
+        v-if="hidden"
+        class="image-container"
+        :href="attachment.url"
         :alt="attachment.description"
-      />
-    </a>
+        :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>
+        <button
+          v-if="remove"
+          class="button-unstyled attachment-button"
+          @click.prevent="onRemove"
+        >
+          <FAIcon icon="trash-alt" />
+        </button>
+      </div>
 
-    <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 === '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 === '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>
+      <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"
+        v-if="type === 'html' && attachment.oembed"
+        class="oembed-container"
+        @click.prevent="linkClicked"
       >
-        <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
+          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>
-    </div>
 
-    <a
-      v-if="type === 'flash' && !hidden"
-      class="flash-container"
-      :href="attachment.url"
-      @click.stop.prevent="openModal"
+      <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>
-- 
cgit v1.2.3-70-g09d2


From 8bab8658e8efd5b8b9f8de9311432c814fa2ef9c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Fri, 18 Jun 2021 16:11:16 +0300
Subject: better handling of unknown files, better upload display

---
 src/components/attachment/attachment.js            |  7 ++---
 src/components/attachment/attachment.vue           | 31 +++++++++++++++++++++-
 src/components/gallery/gallery.js                  | 17 +++++++++++-
 .../post_status_form/post_status_form.vue          |  4 +++
 4 files changed, 54 insertions(+), 5 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index 84656ffa..bd424087 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -65,10 +65,11 @@ const Attachment = {
       return [
         {
           '-loading': this.loading,
-          '-nsfw-placeholder': this.hidden
+          '-nsfw-placeholder': this.hidden,
+          '-editable': this.edit !== undefined
         },
-        '-' + this.type,
-        '-' + this.size,
+        '-type-' + this.type,
+        this.size && '-size-' + this.size,
         `-${this.useContainFit ? 'contain' : 'cover'}-fit`
       ]
     },
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 9b1e83a7..b8be82da 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -15,8 +15,37 @@
       @click.prevent=""
     >
       <FAIcon :icon="placeholderIconClass" />
-      <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }}
+      <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ this.edit ? '' : placeholderName }}
     </a>
+    <div
+      class="attachment-buttons"
+      v-if="edit || remove"
+      >
+      <button
+        v-if="remove"
+        class="button-unstyled attachment-button"
+        @click.prevent="onRemove"
+      >
+        <FAIcon icon="trash-alt" />
+      </button>
+    </div>
+    <div
+      v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
+      class="description-container"
+      :class="{ '-static': !edit }"
+    >
+      <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>
   </button>
   <div
     class="Attachment"
diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js
index ab13f698..741f1de2 100644
--- a/src/components/gallery/gallery.js
+++ b/src/components/gallery/gallery.js
@@ -38,6 +38,13 @@ const Gallery = {
         if (attachment.mimetype.includes('audio')) {
           return [...acc, { audio: true, items: [attachment] }, { items: [] }]
         }
+        if (!(
+          attachment.mimetype.includes('image') ||
+            attachment.mimetype.includes('video') ||
+            attachment.mimetype.includes('flash')
+        )) {
+          return [...acc, { minimal: true, items: [attachment] }, { items: [] }]
+        }
         const maxPerRow = this.maxPerRow || 3
         const attachmentsRemaining = this.attachments.length - i + 1
         const currentRow = acc[acc.length - 1].items
@@ -52,7 +59,15 @@ const Gallery = {
     },
     attachmentsDimensionalScore () {
       return this.rows.reduce((acc, row) => {
-        return acc + (row.audio ? 0.25 : (1 / (row.items.length + 0.6)))
+        let size = 0
+        if (row.minimal) {
+          size += 1 / 8
+        } else if (row.audio) {
+          size += 1 / 4
+        } else {
+          size += 1 / (row.items.length + 0.6)
+        }
+        return acc + size
       }, 0)
     },
     tooManyAttachments () {
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 88ca4c9c..5078ad5f 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -338,6 +338,10 @@
 .post-status-form {
   position: relative;
 
+  .attachments {
+    margin-bottom: 0.5em;
+  }
+
   .form-bottom {
     display: flex;
     justify-content: space-between;
-- 
cgit v1.2.3-70-g09d2


From 4ba8d95a10fdf22a7cefc56ea0515b7c30ee53ff Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 22 Jun 2021 20:33:57 +0300
Subject: fix videos and related not having working drag controls

---
 src/components/attachment/attachment.vue | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index b8be82da..e1e700c4 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -127,7 +127,7 @@
         />
       </a>
 
-      <a
+      <span
         v-if="type === 'video' && !hidden"
         class="video-container"
         :href="attachment.url"
@@ -145,9 +145,9 @@
           class="play-icon"
           icon="play-circle"
         />
-      </a>
+      </span>
 
-      <a
+      <span
         v-if="type === 'audio' && !hidden"
         class="audio-container"
         :href="attachment.url"
@@ -162,7 +162,7 @@
           @play="$emit('play')"
           @pause="$emit('pause')"
         />
-      </a>
+      </span>
 
       <div
         v-if="type === 'html' && attachment.oembed"
@@ -183,7 +183,7 @@
         </div>
       </div>
 
-      <a
+      <span
         v-if="type === 'flash' && !hidden"
         class="flash-container"
         :href="attachment.url"
@@ -196,7 +196,7 @@
           @playerOpened="setFlashLoaded(true)"
           @playerClosed="setFlashLoaded(false)"
         />
-      </a>
+      </span>
     </div>
     <div
       v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
-- 
cgit v1.2.3-70-g09d2


From b67db47c888dd45c49a49b82e7922c2bf40ed61c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Tue, 22 Jun 2021 20:47:35 +0300
Subject: lint

---
 src/components/attachment/attachment.vue             | 16 ++++++++--------
 src/components/gallery/gallery.vue                   | 12 ++++++------
 src/components/post_status_form/post_status_form.vue |  4 ++--
 src/components/status_body/status_body.vue           |  6 +++---
 src/components/status_content/status_content.js      |  1 -
 src/components/status_content/status_content.vue     | 12 ++++++------
 6 files changed, 25 insertions(+), 26 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index e1e700c4..98d9c3ec 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -15,12 +15,12 @@
       @click.prevent=""
     >
       <FAIcon :icon="placeholderIconClass" />
-      <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ this.edit ? '' : placeholderName }}
+      <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ edit ? '' : placeholderName }}
     </a>
     <div
-      class="attachment-buttons"
       v-if="edit || remove"
-      >
+      class="attachment-buttons"
+    >
       <button
         v-if="remove"
         class="button-unstyled attachment-button"
@@ -48,13 +48,13 @@
     </div>
   </button>
   <div
+    v-else
     class="Attachment"
     :class="classNames"
-    v-else
   >
     <div
-      class="attachment-wrapper"
       v-show="!isEmpty"
+      class="attachment-wrapper"
     >
       <a
         v-if="hidden"
@@ -76,9 +76,9 @@
         />
       </a>
       <div
-        class="attachment-buttons"
         v-if="!hidden"
-        >
+        class="attachment-buttons"
+      >
         <button
           v-if="type === 'flash' && flashLoaded"
           class="button-unstyled attachment-button"
@@ -190,8 +190,8 @@
         @click.stop.prevent="openModal"
       >
         <Flash
-          class="flash"
           ref="flash"
+          class="flash"
           :src="attachment.large_thumb_url || attachment.url"
           @playerOpened="setFlashLoaded(true)"
           @playerClosed="setFlashLoaded(false)"
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 5a2de68d..18b94d58 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -1,8 +1,8 @@
 <template>
   <div
-    class="Gallery"
     ref="galleryContainer"
-    :class="{ '-long': tooManyAttachments && hidingLong  }"
+    class="Gallery"
+    :class="{ '-long': tooManyAttachments && hidingLong }"
   >
     <div class="gallery-rows">
       <div
@@ -18,8 +18,8 @@
         >
           <attachment
             v-for="attachment in row.items"
-            class="gallery-item"
             :key="attachment.id"
+            class="gallery-item"
             :nsfw="nsfw"
             :attachment="attachment"
             :allow-play="false"
@@ -28,7 +28,7 @@
             :remove="removeAttachment"
             :edit="editAttachment"
             :description="descriptions && descriptions[attachment.id]"
-            :hideDescription="size === 'small' || tooManyAttachments && hidingLong"
+            :hide-description="size === 'small' || tooManyAttachments && hidingLong"
             :style="itemStyle(attachment.id, row.items)"
             @setMedia="onMedia"
             @naturalSizeLoad="onNaturalSizeLoad"
@@ -41,7 +41,7 @@
       class="many-attachments"
     >
       <div class="many-attachments-text">
-        {{ $t("status.many_attachments", { number: attachments.length })}}
+        {{ $t("status.many_attachments", { number: attachments.length }) }}
       </div>
       <div class="many-attachments-buttons">
         <span
@@ -67,8 +67,8 @@
           </button>
         </span>
         <span
-          class="many-attachments-button"
           v-if="hidingLong"
+          class="many-attachments-button"
         >
           <button
             class="button-unstyled -link"
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index 5ff191cf..87e6d2ab 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -296,8 +296,8 @@
         :descriptions="newStatus.mediaDescriptions"
         :set-media="() => $store.dispatch('setMedia', newStatus.files)"
         :editable="true"
-        :editAttachment="editAttachment"
-        :removeAttachment="removeMediaFile"
+        :edit-attachment="editAttachment"
+        :remove-attachment="removeMediaFile"
         @play="$emit('mediaplay', attachment.id)"
         @pause="$emit('mediapause', attachment.id)"
       />
diff --git a/src/components/status_body/status_body.vue b/src/components/status_body/status_body.vue
index 91ab1797..1001508c 100644
--- a/src/components/status_body/status_body.vue
+++ b/src/components/status_body/status_body.vue
@@ -1,7 +1,7 @@
 <template>
-<div
-  class="StatusBody"
-  :class="{ '-compact': compact }"
+  <div
+    class="StatusBody"
+    :class="{ '-compact': compact }"
   >
     <div class="body">
       <div
diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js
index 55f701d0..9059642e 100644
--- a/src/components/status_content/status_content.js
+++ b/src/components/status_content/status_content.js
@@ -3,7 +3,6 @@ import Poll from '../poll/poll.vue'
 import Gallery from '../gallery/gallery.vue'
 import StatusBody from 'src/components/status_body/status_body.vue'
 import LinkPreview from '../link-preview/link-preview.vue'
-import fileType from 'src/services/file_type/file_type.service'
 import { mapGetters, mapState } from 'vuex'
 import { library } from '@fortawesome/fontawesome-svg-core'
 import {
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index 573eadf5..86b3c52a 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -1,8 +1,8 @@
 <template>
-<div
-  class="StatusContent"
-  :class="{ '-compact': compact }"
->
+  <div
+    class="StatusContent"
+    :class="{ '-compact': compact }"
+  >
     <slot name="header" />
     <StatusBody
       :status="status"
@@ -16,11 +16,11 @@
       </div>
 
       <gallery
-        class="attachments media-body"
         v-if="status.attachments.length !== 0"
+        class="attachments media-body"
         :nsfw="nsfwClickthrough"
         :attachments="status.attachments"
-        :limit="this.compact ? 1 : 0"
+        :limit="compact ? 1 : 0"
         :size="attachmentSize"
         @setMedia="onMedia"
         @play="$emit('mediaplay', attachment.id)"
-- 
cgit v1.2.3-70-g09d2


From 299c00cf74fbef8b8b597330b8c0e6e28765ab7e Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 15 Aug 2021 18:35:26 +0300
Subject: fix video attachments in notifications not having pointer cursor

---
 src/components/attachment/attachment.js  | 3 +++
 src/components/attachment/attachment.vue | 6 ++++--
 2 files changed, 7 insertions(+), 2 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index bd424087..b2ba1655 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -118,6 +118,9 @@ const Attachment = {
       }
       return modalTypes.includes(this.type)
     },
+    videoTag () {
+      return this.useModal ? 'button' : 'span'
+    },
     ...mapGetters(['mergedConfig'])
   },
   watch: {
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 98d9c3ec..8a1c31c7 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -127,9 +127,11 @@
         />
       </a>
 
-      <span
+      <component
+        :is="videoTag"
         v-if="type === 'video' && !hidden"
         class="video-container"
+        :class="{ 'button-unstyled': 'isModal' }"
         :href="attachment.url"
         @click.stop.prevent="openModal"
       >
@@ -145,7 +147,7 @@
           class="play-icon"
           icon="play-circle"
         />
-      </span>
+      </component>
 
       <span
         v-if="type === 'audio' && !hidden"
-- 
cgit v1.2.3-70-g09d2


From 830a03a0d13738ed1677d364fdf03821fbc507ab Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 15 Aug 2021 21:04:28 +0300
Subject: inline description display

---
 src/components/attachment/attachment.js   | 12 +++++++++---
 src/components/attachment/attachment.scss | 13 ++-----------
 src/components/attachment/attachment.vue  |  8 ++++++++
 3 files changed, 19 insertions(+), 14 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index b2ba1655..fea7c6f8 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -15,7 +15,8 @@ import {
   faStop,
   faSearchPlus,
   faTrashAlt,
-  faPencilAlt
+  faPencilAlt,
+  faAlignRight
 } from '@fortawesome/free-solid-svg-icons'
 
 library.add(
@@ -28,7 +29,8 @@ library.add(
   faStop,
   faSearchPlus,
   faTrashAlt,
-  faPencilAlt
+  faPencilAlt,
+  faAlignRight
 )
 
 const Attachment = {
@@ -52,7 +54,8 @@ const Attachment = {
       img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),
       modalOpen: false,
       showHidden: false,
-      flashLoaded: false
+      flashLoaded: false,
+      showDescription: false
     }
   },
   components: {
@@ -157,6 +160,9 @@ const Attachment = {
     setFlashLoaded (event) {
       this.flashLoaded = event
     },
+    toggleDescription () {
+      this.showDescription = !this.showDescription
+    },
     toggleHidden (event) {
       if (
         (this.mergedConfig.useOneClickNsfw && !this.showHidden) &&
diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss
index 33e058e7..065c38d2 100644
--- a/src/components/attachment/attachment.scss
+++ b/src/components/attachment/attachment.scss
@@ -42,19 +42,10 @@
       position: absolute;
       left: 0;
       right: 0;
-      top: 0;
+      bottom: 0;
+      padding-top: 0;
       background: var(--popover);
       box-shadow: var(--popupShadow);
-      opacity: 0;
-      transition: 0.35s all;
-      transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
-    }
-  }
-
-  &:hover {
-    .description-container.-static {
-      opacity: 1;
-      transform: translateY(-3em);
     }
   }
 
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 8a1c31c7..82a72a8d 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -86,6 +86,14 @@
         >
           <FAIcon icon="stop" />
         </button>
+        <button
+          v-if="attachment.description && size !== 'small' && !edit"
+          class="button-unstyled attachment-button"
+          @click.prevent="toggleDescription"
+          :title="$t('status.show_attachment_description')"
+        >
+          <FAIcon icon="align-right" />
+        </button>
         <button
           v-if="!useModal"
           class="button-unstyled attachment-button"
-- 
cgit v1.2.3-70-g09d2


From 0507eb6550830f7b76910d51200675de0aa9b1de Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Sun, 15 Aug 2021 21:04:49 +0300
Subject: ability to move attachments around when making a new post

---
 src/components/attachment/attachment.js            |  8 ++++++++
 src/components/attachment/attachment.vue           | 24 ++++++++++++++++++++--
 src/components/gallery/gallery.js                  |  2 ++
 src/components/gallery/gallery.vue                 | 10 +++++----
 .../post_status_form/post_status_form.js           | 13 +++++++++++-
 .../post_status_form/post_status_form.vue          |  2 ++
 src/i18n/en.json                                   |  7 +++++++
 7 files changed, 59 insertions(+), 7 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index fea7c6f8..f9c8d981 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -42,6 +42,8 @@ const Attachment = {
     'size',
     'setMedia',
     'remove',
+    'shiftUp',
+    'shiftDn',
     'edit'
   ],
   data () {
@@ -154,6 +156,12 @@ const Attachment = {
     onRemove () {
       this.remove && this.remove(this.attachment)
     },
+    onShiftUp () {
+      this.shiftUp && this.shiftUp(this.attachment)
+    },
+    onShiftDn () {
+      this.shiftDn && this.shiftDn(this.attachment)
+    },
     stopFlash () {
       this.$refs.flash.closePlayer()
     },
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 82a72a8d..4abf63dd 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -30,7 +30,7 @@
       </button>
     </div>
     <div
-      v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
+      v-if="size !== 'hide' && !hideDescription && (edit || localDescription || showDescription)"
       class="description-container"
       :class="{ '-static': !edit }"
     >
@@ -83,6 +83,7 @@
           v-if="type === 'flash' && flashLoaded"
           class="button-unstyled attachment-button"
           @click.prevent="stopFlash"
+          :title="$t('status.attachment_stop_flash')"
         >
           <FAIcon icon="stop" />
         </button>
@@ -98,6 +99,7 @@
           v-if="!useModal"
           class="button-unstyled attachment-button"
           @click.prevent="openModalForce"
+          :title="$t('status.show_attachment_in_modal')"
         >
           <FAIcon icon="search-plus" />
         </button>
@@ -105,13 +107,31 @@
           v-if="nsfw && hideNsfwLocal"
           class="button-unstyled attachment-button"
           @click.prevent="toggleHidden"
+          :title="$t('status.hide_attachment')"
         >
           <FAIcon icon="times" />
         </button>
+        <button
+          v-if="shiftUp"
+          class="button-unstyled attachment-button"
+          @click.prevent="onShiftUp"
+          :title="$t('status.move_up')"
+        >
+          <FAIcon icon="chevron-left" />
+        </button>
+        <button
+          v-if="shiftDn"
+          class="button-unstyled attachment-button"
+          @click.prevent="onShiftDn"
+          :title="$t('status.move_down')"
+        >
+          <FAIcon icon="chevron-right" />
+        </button>
         <button
           v-if="remove"
           class="button-unstyled attachment-button"
           @click.prevent="onRemove"
+          :title="$t('status.remove_attachment')"
         >
           <FAIcon icon="trash-alt" />
         </button>
@@ -209,7 +229,7 @@
       </span>
     </div>
     <div
-      v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
+      v-if="size !== 'hide' && !hideDescription && (edit || (localDescription && showDescription))"
       class="description-container"
       :class="{ '-static': !edit }"
     >
diff --git a/src/components/gallery/gallery.js b/src/components/gallery/gallery.js
index 15436d61..094b3e57 100644
--- a/src/components/gallery/gallery.js
+++ b/src/components/gallery/gallery.js
@@ -12,6 +12,8 @@ const Gallery = {
     'size',
     'editable',
     'removeAttachment',
+    'shiftUpAttachment',
+    'shiftDnAttachment',
     'editAttachment',
     'grid'
   ],
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index 18b94d58..f9cad8a9 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -6,8 +6,8 @@
   >
     <div class="gallery-rows">
       <div
-        v-for="(row, index) in rows"
-        :key="index"
+        v-for="(row, rowIndex) in rows"
+        :key="rowIndex"
         class="gallery-row"
         :style="rowStyle(row)"
         :class="{ '-audio': row.audio, '-minimal': row.minimal, '-grid': grid }"
@@ -16,8 +16,8 @@
           class="gallery-row-inner"
           :class="{ '-grid': grid }"
         >
-          <attachment
-            v-for="attachment in row.items"
+          <Attachment
+            v-for="(attachment, attachmentIndex) in row.items"
             :key="attachment.id"
             class="gallery-item"
             :nsfw="nsfw"
@@ -26,6 +26,8 @@
             :size="size"
             :editable="editable"
             :remove="removeAttachment"
+            :shiftUp="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
+            :shiftDn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
             :edit="editAttachment"
             :description="descriptions && descriptions[attachment.id]"
             :hide-description="size === 'small' || tooManyAttachments && hidingLong"
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 1a12db9c..fe07309f 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -391,9 +391,20 @@ const PostStatusForm = {
       this.$emit('resize')
     },
     editAttachment (fileInfo, newText) {
-      console.log(fileInfo, newText)
       this.newStatus.mediaDescriptions[fileInfo.id] = newText
     },
+    shiftUpMediaFile (fileInfo) {
+      const { files } = this.newStatus
+      const index = this.newStatus.files.indexOf(fileInfo)
+      files.splice(index, 1)
+      files.splice(index - 1, 0, fileInfo)
+    },
+    shiftDnMediaFile (fileInfo) {
+      const { files } = this.newStatus
+      const index = this.newStatus.files.indexOf(fileInfo)
+      files.splice(index, 1)
+      files.splice(index + 1, 0, fileInfo)
+    },
     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 87e6d2ab..2e0980a2 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -298,6 +298,8 @@
         :editable="true"
         :edit-attachment="editAttachment"
         :remove-attachment="removeMediaFile"
+        :shift-up-attachment="newStatus.files.length > 1 && shiftUpMediaFile"
+        :shift-dn-attachment="newStatus.files.length > 1 && shiftDnMediaFile"
         @play="$emit('mediaplay', attachment.id)"
         @pause="$emit('mediapause', attachment.id)"
       />
diff --git a/src/i18n/en.json b/src/i18n/en.json
index a4f24c64..0ccd0da7 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -721,6 +721,13 @@
     "many_attachments": "Post has {number} attachment(s)",
     "collapse_attachments": "Collapse attachments",
     "show_all_attachments": "Show all attachments",
+    "show_attachment_in_modal": "Show in media modal",
+    "show_attachment_description": "Preview description (open attachment for full description)",
+    "hide_attachment": "Hide attachment",
+    "remove_attachment": "Remove attachment",
+    "attachment_stop_flash": "Stop Flash player",
+    "move_up": "Shift attachment left",
+    "move_down": "Shift attachment right",
     "open_gallery": "Open gallery"
   },
   "user_card": {
-- 
cgit v1.2.3-70-g09d2


From befd4d5fc787d3f8287456dbf5d1a6c70419591f Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Thu, 10 Feb 2022 15:42:28 +0200
Subject: improve unknown attachment handling

---
 src/components/attachment/attachment.js   |  7 ++++---
 src/components/attachment/attachment.scss | 10 ++++++++++
 src/components/attachment/attachment.vue  | 17 ++++++++++++++---
 3 files changed, 28 insertions(+), 6 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index f9c8d981..d62a4adc 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -79,7 +79,7 @@ const Attachment = {
       ]
     },
     usePlaceholder () {
-      return this.size === 'hide' || this.type === 'unknown'
+      return this.size === 'hide'
     },
     useContainFit () {
       return this.$store.getters.mergedConfig.useContainFit
@@ -106,7 +106,7 @@ const Attachment = {
       return this.nsfw && this.hideNsfwLocal && !this.showHidden
     },
     isEmpty () {
-      return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown'
+      return (this.type === 'html' && !this.attachment.oembed)
     },
     useModal () {
       let modalTypes = []
@@ -143,6 +143,8 @@ const Attachment = {
       if (this.useModal) {
         this.$emit('setMedia')
         this.$store.dispatch('setCurrentMedia', this.attachment)
+      } else if (this.type === 'unknown') {
+        window.open(this.attachment.url)
       }
     },
     openModalForce (event) {
@@ -150,7 +152,6 @@ const Attachment = {
       this.$store.dispatch('setCurrentMedia', this.attachment)
     },
     onEdit (event) {
-      console.log('ONEDIT', event)
       this.edit && this.edit(this.attachment, event)
     },
     onRemove () {
diff --git a/src/components/attachment/attachment.scss b/src/components/attachment/attachment.scss
index 065c38d2..dfda15bf 100644
--- a/src/components/attachment/attachment.scss
+++ b/src/components/attachment/attachment.scss
@@ -54,6 +54,7 @@
     min-width: 0;
   }
 
+  & .placeholder-container,
   & .image-container,
   & .audio-container,
   & .video-container,
@@ -93,6 +94,15 @@
     }
   }
 
+  .placeholder-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding-top: 0.5em;
+  }
+
+
   .play-icon {
     position: absolute;
     font-size: 64px;
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 4abf63dd..59173759 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -12,7 +12,6 @@
       :href="attachment.url"
       :alt="attachment.description"
       :title="attachment.description"
-      @click.prevent=""
     >
       <FAIcon :icon="placeholderIconClass" />
       <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ edit ? '' : placeholderName }}
@@ -88,7 +87,7 @@
           <FAIcon icon="stop" />
         </button>
         <button
-          v-if="attachment.description && size !== 'small' && !edit"
+          v-if="attachment.description && size !== 'small' && !edit && type !== 'unknown'"
           class="button-unstyled attachment-button"
           @click.prevent="toggleDescription"
           :title="$t('status.show_attachment_description')"
@@ -96,7 +95,7 @@
           <FAIcon icon="align-right" />
         </button>
         <button
-          v-if="!useModal"
+          v-if="!useModal && type !== 'unknown'"
           class="button-unstyled attachment-button"
           @click.prevent="openModalForce"
           :title="$t('status.show_attachment_in_modal')"
@@ -155,6 +154,18 @@
         />
       </a>
 
+      <a
+        v-if="type === 'unknown' && !hidden"
+        class="placeholder-container"
+        :href="attachment.url"
+        target="_blank"
+      >
+        <FAIcon size="5x" :icon="placeholderIconClass" />
+        <p>
+          {{ localDescription }}
+        </p>
+      </a>
+
       <component
         :is="videoTag"
         v-if="type === 'video' && !hidden"
-- 
cgit v1.2.3-70-g09d2


From 77b55a559be26e431f68188058c7900c2e16682c Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 28 Feb 2022 19:42:02 +0200
Subject: fix placeholder attachments opening new tab

---
 src/components/attachment/attachment.vue | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 59173759..d1d43ac3 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -12,6 +12,7 @@
       :href="attachment.url"
       :alt="attachment.description"
       :title="attachment.description"
+      @click.prevent
     >
       <FAIcon :icon="placeholderIconClass" />
       <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ edit ? '' : placeholderName }}
-- 
cgit v1.2.3-70-g09d2


From 7a8c975f616f745b57a8426e40d7573d08b8416d Mon Sep 17 00:00:00 2001
From: Tusooa Zhu <tusooa@kazv.moe>
Date: Sun, 6 Mar 2022 14:22:59 -0500
Subject: Lint

---
 src/components/attachment/attachment.vue | 19 +++++++++++--------
 src/components/gallery/gallery.vue       |  4 ++--
 2 files changed, 13 insertions(+), 10 deletions(-)

(limited to 'src/components/attachment/attachment.vue')

diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index d1d43ac3..2a89886d 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -82,56 +82,56 @@
         <button
           v-if="type === 'flash' && flashLoaded"
           class="button-unstyled attachment-button"
-          @click.prevent="stopFlash"
           :title="$t('status.attachment_stop_flash')"
+          @click.prevent="stopFlash"
         >
           <FAIcon icon="stop" />
         </button>
         <button
           v-if="attachment.description && size !== 'small' && !edit && type !== 'unknown'"
           class="button-unstyled attachment-button"
-          @click.prevent="toggleDescription"
           :title="$t('status.show_attachment_description')"
+          @click.prevent="toggleDescription"
         >
           <FAIcon icon="align-right" />
         </button>
         <button
           v-if="!useModal && type !== 'unknown'"
           class="button-unstyled attachment-button"
-          @click.prevent="openModalForce"
           :title="$t('status.show_attachment_in_modal')"
+          @click.prevent="openModalForce"
         >
           <FAIcon icon="search-plus" />
         </button>
         <button
           v-if="nsfw && hideNsfwLocal"
           class="button-unstyled attachment-button"
-          @click.prevent="toggleHidden"
           :title="$t('status.hide_attachment')"
+          @click.prevent="toggleHidden"
         >
           <FAIcon icon="times" />
         </button>
         <button
           v-if="shiftUp"
           class="button-unstyled attachment-button"
-          @click.prevent="onShiftUp"
           :title="$t('status.move_up')"
+          @click.prevent="onShiftUp"
         >
           <FAIcon icon="chevron-left" />
         </button>
         <button
           v-if="shiftDn"
           class="button-unstyled attachment-button"
-          @click.prevent="onShiftDn"
           :title="$t('status.move_down')"
+          @click.prevent="onShiftDn"
         >
           <FAIcon icon="chevron-right" />
         </button>
         <button
           v-if="remove"
           class="button-unstyled attachment-button"
-          @click.prevent="onRemove"
           :title="$t('status.remove_attachment')"
+          @click.prevent="onRemove"
         >
           <FAIcon icon="trash-alt" />
         </button>
@@ -161,7 +161,10 @@
         :href="attachment.url"
         target="_blank"
       >
-        <FAIcon size="5x" :icon="placeholderIconClass" />
+        <FAIcon
+          size="5x"
+          :icon="placeholderIconClass"
+        />
         <p>
           {{ localDescription }}
         </p>
diff --git a/src/components/gallery/gallery.vue b/src/components/gallery/gallery.vue
index f9cad8a9..f2e1b5ce 100644
--- a/src/components/gallery/gallery.vue
+++ b/src/components/gallery/gallery.vue
@@ -26,8 +26,8 @@
             :size="size"
             :editable="editable"
             :remove="removeAttachment"
-            :shiftUp="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
-            :shiftDn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
+            :shift-up="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
+            :shift-dn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
             :edit="editAttachment"
             :description="descriptions && descriptions[attachment.id]"
             :hide-description="size === 'small' || tooManyAttachments && hidingLong"
-- 
cgit v1.2.3-70-g09d2

