feat: kickstart media bubble

This commit is contained in:
Shivam Mishra
2024-11-27 19:05:07 +05:30
parent 230279cafe
commit 226d7ceb03
4 changed files with 104 additions and 11 deletions
@@ -13,10 +13,25 @@ import Avatar from 'next/avatar/Avatar.vue';
import TextBubble from './bubbles/Text.vue';
import ActivityBubble from './bubbles/Activity.vue';
import MediaBubble from './bubbles/Media.vue';
import MessageError from './MessageError.vue';
import MessageMeta from './MessageMeta.vue';
/**
* @typedef {Object} Attachment
* @property {number} id - Unique identifier for the attachment
* @property {number} messageId - ID of the associated message
* @property {'image'|'audio'|'video'|'file'|'location'|'fallback'|'share'|'story_mention'|'contact'|'ig_reel'} fileType - Type of the attachment (file or image)
* @property {number} accountId - ID of the associated account
* @property {string|null} extension - File extension
* @property {string} dataUrl - URL to access the full attachment data
* @property {string} thumbUrl - URL to access the thumbnail version
* @property {number} fileSize - Size of the file in bytes
* @property {number|null} width - Width of the image if applicable
* @property {number|null} height - Height of the image if applicable
*/
/**
* @typedef {Object} Sender
* @property {Object} additional_attributes - Additional attributes of the sender
@@ -37,18 +52,19 @@ import MessageMeta from './MessageMeta.vue';
/**
* @typedef {Object} Props
* @property {('sent'|'delivered'|'read'|'failed')} status - The delivery status of the message
* @property {ContentAttributes} [contentAttributes={}] - Additional attributes of the message content
* @property {Attachment[]} [attachments=[]] - The attachments associated with the message
* @property {Sender|null} [sender=null] - The sender information
* @property {boolean} [private=false] - Whether the message is private
* @property {number|null} [senderId=null] - The ID of the sender
* @property {number} createdAt - Timestamp when the message was created
* @property {number} currentUserId - The ID of the current user
* @property {number} id - The unique identifier for the message
* @property {number} messageType - The type of message (must be one of MESSAGE_TYPES)
* @property {('sent'|'delivered'|'read'|'failed')} status - The delivery status of the message
* @property {boolean} [private=false] - Whether the message is private
* @property {number} createdAt - Timestamp when the message was created
* @property {Sender|null} [sender=null] - The sender information
* @property {ContentAttributes} [contentAttributes={}] - Additional attributes of the message content
* @property {number|null} [senderId=null] - The ID of the sender
* @property {string|null} [senderType=null] - The type of the sender
* @property {string|null} [error=null] - Error message if the message failed to send
* @property {string|null} [senderType=null] - The type of the sender
* @property {string} content - The message content
* @property {number} currentUserId - The ID of the current user
*/
const props = defineProps({
@@ -63,6 +79,10 @@ const props = defineProps({
required: true,
validator: value => Object.values(MESSAGE_STATUS).includes(value),
},
attachments: {
type: Array,
default: () => [],
},
private: {
type: Boolean,
default: false,
@@ -188,6 +208,14 @@ const shouldShowAvatar = computed(() => {
return true;
});
const componentToRender = computed(() => {
if (props.attachments.length === 1) {
return MediaBubble;
}
return TextBubble;
});
provideMessageContext({
variant,
orientation,
@@ -218,7 +246,11 @@ provideMessageContext({
>
<Avatar :name="sender ? sender.name : ''" src="" :size="24" />
</div>
<TextBubble :content="content" class="[grid-area:bubble]" />
<Component
:is="componentToRender"
v-bind="props"
class="[grid-area:bubble]"
/>
<MessageError
v-if="contentAttributes.externalError"
class="[grid-area:meta]"
@@ -0,0 +1,48 @@
<script setup>
import { ref, computed } from 'vue';
import BaseBubble from './Base.vue';
import Button from 'next/button/Button.vue';
/**
* @typedef {Object} Attachment
* @property {number} id - Unique identifier for the attachment
* @property {number} messageId - ID of the associated message
* @property {'image'|'audio'|'video'|'file'|'location'|'fallback'|'share'|'story_mention'|'contact'|'ig_reel'} fileType - Type of the attachment (file or image)
* @property {number} accountId - ID of the associated account
* @property {string|null} extension - File extension
* @property {string} dataUrl - URL to access the full attachment data
* @property {string} thumbUrl - URL to access the thumbnail version
* @property {number} fileSize - Size of the file in bytes
* @property {number|null} width - Width of the image if applicable
* @property {number|null} height - Height of the image if applicable
*/
/**
* @typedef {Object} Props
* @property {Attachment[]} [attachments=[]] - The attachments associated with the message
*/
const props = defineProps({
attachments: {
type: Array,
required: true,
},
});
const attachment = computed(() => {
return props.attachments[0];
});
const hasError = ref(false);
</script>
<template>
<BaseBubble class="overflow-hidden relative group">
<img :src="attachment.thumbUrl" @click="onClick" @error="hasError = true" />
<div
class="inset-0 absolute bg-gradient-to-t from-n-slate-12/10 to-transparent hidden group-hover:flex items-end justify-end"
>
<Button xs faded slate icon="i-lucide-download" />
</div>
</BaseBubble>
</template>
@@ -33,3 +33,16 @@ export const MESSAGE_STATUS = {
FAILED: 'failed',
PROGRESS: 'progress',
};
export const ATTACHMENT_TYPES = {
IMAGE: 'image',
AUDIO: 'audio',
VIDEO: 'video',
FILE: 'file',
LOCATION: 'location',
FALLBACK: 'fallback',
SHARE: 'share',
STORY_MENTION: 'story_mention',
CONTACT: 'contact',
IG_REEL: 'ig_reel',
};
@@ -332,9 +332,9 @@ export default camelcaseKeys(
account_id: 2,
extension: null,
data_url:
'http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBaUFLIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--0f76a0caba1df9a5217630b5ca1fdfb09c9111d3/image.png',
'https://images.pexels.com/photos/28379994/pexels-photo-28379994/free-photo-of-a-laptop-with-a-yellow-phone-on-top-of-it.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2',
thumb_url:
'http://localhost:3000/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBaUFLIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--0f76a0caba1df9a5217630b5ca1fdfb09c9111d3/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RTNKbGMybDZaVjkwYjE5bWFXeHNXd2RwQWZvdyIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--ebe60765d222d11ade39165eae49cc4b2de18d89/image.png',
'https://images.pexels.com/photos/28379994/pexels-photo-28379994/free-photo-of-a-laptop-with-a-yellow-phone-on-top-of-it.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2',
file_size: 983912,
width: null,
height: null,