Skip to content

Commit

Permalink
feat: file attachments for images
Browse files Browse the repository at this point in the history
  • Loading branch information
Silver-IT committed Mar 6, 2024
1 parent d6c17f2 commit 30ef55c
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 22 deletions.
2 changes: 0 additions & 2 deletions frontend/views/containers/chatroom/ChatMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ import { mapGetters } from 'vuex'
import { Vue } from '@common/common.js'
import Avatar from '@components/Avatar.vue'
import InfiniteLoading from 'vue-infinite-loading'
import Loading from '@components/Loading.vue'
import Message from './Message.vue'
import MessageInteractive from './MessageInteractive.vue'
import MessageNotification from './MessageNotification.vue'
Expand Down Expand Up @@ -212,7 +211,6 @@ export default ({
ConversationGreetings,
Emoticons,
InfiniteLoading,
Loading,
Message,
MessageInteractive,
MessageNotification,
Expand Down
4 changes: 0 additions & 4 deletions frontend/views/containers/chatroom/MessageBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,6 @@ export default ({
isMention (o) {
return o.type === TextObjectType.Mention
},
isImage (attachment) {
return attachment.attachType === 'image'
},
generateTextObjectsFromText (text) {
if (!text) {
return []
Expand Down Expand Up @@ -253,7 +250,6 @@ export default ({
padding: 0.5rem 1.25rem;
}
position: relative;
max-height: 100%;
&.removed {
background-color: $danger_2;
Expand Down
4 changes: 2 additions & 2 deletions frontend/views/containers/chatroom/SendArea.vue
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ import Avatar from '@components/Avatar.vue'
import Tooltip from '@components/Tooltip.vue'
import ChatAttachmentPreview from './file-attachment/ChatAttachmentPreview.vue'
import { makeMentionFromUsername } from '@model/contracts/shared/functions.js'
import { CHATROOM_PRIVACY_LEVEL } from '@model/contracts/shared/constants.js'
import { CHATROOM_PRIVACY_LEVEL, ATTACHMENT_TYPES } from '@model/contracts/shared/constants.js'
import { CHAT_ATTACHMENT_SUPPORTED_EXTENSIONS, CHAT_ATTACHMENT_SIZE_LIMIT } from '~/frontend/utils/constants.js'
import { OPEN_MODAL, CHATROOM_USER_TYPING, CHATROOM_USER_STOP_TYPING } from '@utils/events.js'
import { uniq, throttle, cloneDeep, randomHexString } from '@model/contracts/shared/giLodash.js'
Expand Down Expand Up @@ -626,7 +626,7 @@ export default ({
extension: fileExt,
mimeType: file.type || '',
attachmentId: randomHexString(15),
attachType: file.type.match('image/') ? 'image' : 'non-image',
attachType: file.type.match('image/') ? ATTACHMENT_TYPES.IMAGE : ATTACHMENT_TYPES.NON_IMAGE,
downloadData: null // NOTE: we can tell if the attachment has been uploaded by seeing if this field is non-null.
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,25 @@
class='is-download-item'
tabindex='0'
)
.c-preview-non-image
.c-preview-non-image(v-if='!shouldPreviewImages')
.c-non-image-icon
i.icon-file

.c-non-image-file-info
.c-file-name.has-ellipsis {{ entry.name }}
.c-file-ext {{ fileExt(entry) }}

.c-preview-img(v-else)
img(
v-if='preloadedBlobs[entry.attachmentId]'
:src='preloadedBlobs[entry.attachmentId].url'
:alt='entry.name'
)
.loading-box(v-else)

.c-attachment-actions-wrapper(
:class='{ "is-for-image": shouldPreviewImages }'
)
.c-attachment-actions
tooltip(
direction='top'
Expand Down Expand Up @@ -69,6 +80,7 @@
<script>
import sbp from '@sbp/sbp'
import Tooltip from '@components/Tooltip.vue'
import { ATTACHMENT_TYPES } from '@model/contracts/shared/constants.js'
export default {
name: 'ChatAttachmentPreview',
Expand All @@ -88,7 +100,24 @@ export default {
},
data () {
return {
isPreparingDownload: []
isPreparingDownload: [],
preloadedBlobs: {}
}
},
computed: {
shouldPreviewImages () {
return !this.attachmentList.some(attachment => attachment.attachType === ATTACHMENT_TYPES.NON_IMAGE)
}
},
mounted () {
if (this.shouldPreviewImages) {
(async () => {
for await (const attachment of this.attachmentList) {
const blobWithURL = await this.getBlobWithURLFromAttachment(attachment)
this.preloadedBlobs[attachment.attachmentId] = blobWithURL
}
this.$forceUpdate()
})()
}
},
methods: {
Expand All @@ -100,19 +129,27 @@ export default {
deleteAttachment (attachment) {
console.log('TODO - delete attachment')
},
async getBlobWithURLFromAttachment (attachment) {
if (!attachment.downloadData) { return }
const blob = await sbp('chelonia/fileDownload', attachment.downloadData)
const url = URL.createObjectURL(blob)
return { blob, url }
},
async downloadAttachment (attachment) {
if (!attachment.downloadData) { return }
// reference: https://blog.logrocket.com/programmatically-downloading-files-browser/
const { downloadData, name, attachmentId } = attachment
const { name, attachmentId } = attachment
this.isPreparingDownload = [...this.isPreparingDownload, attachmentId]
try {
const blob = await sbp('chelonia/fileDownload', downloadData)
const url = URL.createObjectURL(blob)
let blobWithURL = this.preloadedBlobs[attachment.attachmentId]
if (!blobWithURL) {
blobWithURL = await this.getBlobWithURLFromAttachment(attachment)
}
const aTag = this.$refs.downloadHelper
aTag.setAttribute('href', url)
aTag.setAttribute('href', blobWithURL.url)
aTag.setAttribute('download', name)
aTag.click()
Expand Down Expand Up @@ -145,21 +182,36 @@ export default {
width: calc(100% - 4rem);
}
.c-attachment-actions {
.c-attachment-actions-wrapper {
display: none;
position: absolute;
right: 0.5rem;
display: none;
gap: 0.25rem;
background-color: $background_0;
padding: 2px;
top: 0;
bottom: 0;
.is-icon-small {
border-radius: 0;
.c-attachment-actions {
display: flex;
gap: 0.25rem;
align-self: center;
align-items: center;
background-color: $background_0;
padding: 2px;
.is-icon-small {
border-radius: 0;
}
}
&.is-for-image {
.c-attachment-actions {
align-self: flex-start;
margin-top: 0.5rem;
}
}
}
.is-download-item {
&:hover .c-attachment-actions {
&:hover .c-attachment-actions-wrapper {
display: flex;
}
Expand All @@ -168,6 +220,33 @@ export default {
min-width: 16rem;
min-height: 3.5rem;
}
.c-preview-img {
padding: 0.5rem;
img {
max-width: 100%;
max-height: 20rem;
@include phone {
max-height: 12rem;
}
}
.loading-box {
border-radius: 0;
width: 24rem;
margin-bottom: 0;
@include tablet {
width: 20rem;
}
@include phone {
width: 16rem;
}
}
}
}
}
}
Expand Down

0 comments on commit 30ef55c

Please sign in to comment.