feat: add ig bubble
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
import Message from './Message.vue';
|
||||
|
||||
// import textOnly from './fixtures/textOnly.js';
|
||||
import instagramConversation from './fixtures/instagramConversation.js';
|
||||
|
||||
const messages = instagramConversation;
|
||||
|
||||
const shouldGroupWithNext = index => {
|
||||
if (index === messages.length - 1) return false;
|
||||
|
||||
const current = messages[index];
|
||||
const next = messages[index + 1];
|
||||
|
||||
if (next.status === 'failed') return false;
|
||||
|
||||
const nextSenderId = next.senderId ?? next.sender?.id;
|
||||
const currentSenderId = current.senderId ?? current.sender?.id;
|
||||
if (currentSenderId !== nextSenderId) return false;
|
||||
|
||||
// Check if messages are in the same minute by rounding down to nearest minute
|
||||
return Math.floor(next.createdAt / 60) === Math.floor(current.createdAt / 60);
|
||||
};
|
||||
|
||||
const getReplyToMessage = message => {
|
||||
const idToCheck = message.contentAttributes.inReplyTo;
|
||||
if (!idToCheck) return null;
|
||||
|
||||
return messages.find(candidate => idToCheck === candidate.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story title="Components/Messages/Instagram" :layout="{ type: 'single' }">
|
||||
<div class="p-4 bg-n-background rounded-lg w-full min-w-5xl grid">
|
||||
<template v-for="(message, index) in messages" :key="message.id">
|
||||
<Message
|
||||
:current-user-id="1"
|
||||
:group-with-next="shouldGroupWithNext(index)"
|
||||
:in-reply-to="getReplyToMessage(message)"
|
||||
v-bind="message"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -17,6 +17,7 @@ import ActivityBubble from './bubbles/Activity.vue';
|
||||
import ImageBubble from './bubbles/Image.vue';
|
||||
import FileBubble from './bubbles/File.vue';
|
||||
import AudioBubble from './bubbles/Audio.vue';
|
||||
import VideoBubble from './bubbles/Video.vue';
|
||||
import AttachmentsBubble from './bubbles/Attachments.vue';
|
||||
import EmailBubble from './bubbles/Email/Index.vue';
|
||||
|
||||
@@ -242,6 +243,8 @@ const componentToRender = computed(() => {
|
||||
if (fileType === ATTACHMENT_TYPES.IMAGE) return ImageBubble;
|
||||
if (fileType === ATTACHMENT_TYPES.FILE) return FileBubble;
|
||||
if (fileType === ATTACHMENT_TYPES.AUDIO) return AudioBubble;
|
||||
if (fileType === ATTACHMENT_TYPES.VIDEO) return VideoBubble;
|
||||
if (fileType === ATTACHMENT_TYPES.IG_REEL) return VideoBubble;
|
||||
}
|
||||
|
||||
if (props.attachments.length > 1 && !props.content) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import BaseBubble from './Base.vue';
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
import { ATTACHMENT_TYPES } from '../constants';
|
||||
|
||||
/**
|
||||
* @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 isReel = computed(() => {
|
||||
return attachment.value.fileType === ATTACHMENT_TYPES.IG_REEL;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseBubble class="overflow-hidden relative group">
|
||||
<div
|
||||
v-if="isReel"
|
||||
class="absolute p-2 flex items-start justify-end size-12 bg-gradient-to-bl from-n-alpha-black1 to-transparent right-0"
|
||||
>
|
||||
<Icon icon="i-lucide-instagram" class="text-white" />
|
||||
</div>
|
||||
<video
|
||||
controls
|
||||
:src="attachment.dataUrl"
|
||||
:class="{
|
||||
'max-w-48': isReel,
|
||||
'max-w-full': !isReel,
|
||||
}"
|
||||
@error="hasError = true"
|
||||
/>
|
||||
</BaseBubble>
|
||||
</template>
|
||||
Reference in New Issue
Block a user