Overview
The Chat Store is the core of the messaging functionality in Bimble Chat Vue. Built using Pinia's Composition API, it manages chat conversations, message history, search functionality, and conversation states with full TypeScript support.
Key Features
Reactive chat list management, real-time search, conversation state tracking, and seamless integration with the mock API system.
Store Structure
The Chat Store uses the Composition API pattern and is located at src/stores/modules/chat.ts
:
import { defineStore } from 'pinia';
import { ref } from 'vue';
import axios from '@/utils/axios';
import type { Chat } from '@/types/chat';
export const useChatStore = defineStore('chat', () => {
// State
const chats = ref<Chat[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const isConversationActive = ref(false);
// Actions
const setConversationActive = (active: boolean) => {
isConversationActive.value = active;
};
const fetchChats = async () => {
// Fetch logic...
};
const searchChats = async (query: string) => {
// Search logic...
};
return {
chats,
loading,
error,
isConversationActive,
setConversationActive,
fetchChats,
searchChats
};
});
State Properties
The Chat Store maintains the following reactive state properties:
chats
Array of chat conversations with full message history and metadata
loading
Boolean flag indicating if any async operation is in progress
error
Error message from failed operations, null when no error
isConversationActive
Tracks whether a conversation view is currently active
Type Definitions
The Chat Store uses strong TypeScript typing with interfaces defined in src/types/chat.ts
:
export interface Chat {
id: number;
name: string;
avatar: string;
lastMessage: string;
timestamp: Date;
unreadCount: number;
status: 'online' | 'offline' | 'away' | 'busy';
isPinned: boolean;
type: 'single' | 'group' | 'archived';
email?: string;
username?: string;
lastSeen?: Date;
chatHistory?: ChatMessage[];
}
export interface ChatMessage {
id: string;
senderId: number;
msg: string;
createdAt: Date;
type: string;
attachment: any[];
replyTo?: {
id: string;
msg: string;
senderName: string;
};
}
export interface Message {
id: number;
chatId: number;
senderId: number;
content: string;
timestamp: string;
messageType: 'text' | 'image' | 'file' | 'voice';
isRead: boolean;
}
Actions
The Chat Store provides several actions for managing chat functionality:
fetchChats()
Fetches all chat conversations from the API and transforms the data
const fetchChats = async () => {
loading.value = true;
error.value = null;
try {
const response = await axios.get('/api/data/chat/ChatData');
chats.value = response.data.map((chatData: any) => ({
id: chatData.id,
name: chatData.name,
avatar: chatData.thumb,
lastMessage: chatData.chatHistory[chatData.chatHistory.length - 1]?.msg || '',
timestamp: chatData.chatHistory[chatData.chatHistory.length - 1]?.createdAt || new Date(),
unreadCount: Math.floor(Math.random() * 5),
status: chatData.status,
isPinned: chatData.recent,
type: chatData.type,
email: chatData.email,
username: chatData.username,
lastSeen: chatData.lastSeen,
chatHistory: chatData.chatHistory
}));
} catch (err) {
error.value = 'Failed to fetch chats';
} finally {
loading.value = false;
}
};
searchChats(query)
Searches through chats by name and message content
const searchChats = async (query: string) => {
loading.value = true;
error.value = null;
try {
const response = await axios.get('/api/data/chat/ChatData');
const filteredData = response.data.filter((chatData: any) =>
chatData.name.toLowerCase().includes(query.toLowerCase()) ||
chatData.chatHistory.some((msg: any) =>
msg.msg.toLowerCase().includes(query.toLowerCase())
)
);
chats.value = filteredData.map((chatData: any) => ({
// Transform filtered data...
}));
} catch (err) {
error.value = 'Failed to search chats';
} finally {
loading.value = false;
}
};
setConversationActive()
Updates the conversation active state for UI management
const setConversationActive = (active: boolean) => {
isConversationActive.value = active;
};
Usage: Called when opening/closing conversation views to manage UI state and responsive behavior.
Usage Examples
Here are practical examples of using the Chat Store in Vue components:
Basic Setup in Component
Import and initialize the Chat Store in your Vue component:
<script setup lang="ts">
import { onMounted, computed } from 'vue'
import { useChatStore } from '@/stores/modules/chat'
const chatStore = useChatStore()
// Reactive computed properties
const chats = computed(() => chatStore.chats)
const isLoading = computed(() => chatStore.loading)
const hasError = computed(() => chatStore.error)
// Load chats on component mount
onMounted(async () => {
await chatStore.fetchChats()
})
</script>
Chat List Component
Display the chat list with search functionality:
<template>
<div class="chat-list">
<!-- Search Input -->
<v-text-field
v-model="searchQuery"
@input="handleSearch"
placeholder="Search conversations..."
prepend-inner-icon="mdi-magnify"
/>
<!-- Loading State -->
<v-progress-circular v-if="isLoading" indeterminate />
<!-- Error State -->
<v-alert v-if="hasError" type="error">
{{ hasError }}
</v-alert>
<!-- Chat List -->
<v-list v-if="!isLoading && !hasError">
<v-list-item
v-for="chat in chats"
:key="chat.id"
@click="openChat(chat)"
>
<template #prepend>
<v-avatar>
<v-img :src="chat.avatar" :alt="chat.name" />
</v-avatar>
</template>
<v-list-item-title>{{ chat.name }}</v-list-item-title>
<v-list-item-subtitle>{{ chat.lastMessage }}</v-list-item-subtitle>
<template #append>
<div class="chat-meta">
<span class="timestamp">{{ formatTime(chat.timestamp) }}</span>
<v-chip v-if="chat.unreadCount > 0" size="small" color="primary">
{{ chat.unreadCount }}
</v-chip>
</div>
</template>
</v-list-item>
</v-list>
</div>
</template>
Search Functionality
Implement real-time search with debouncing:
import { ref, watch } from 'vue'
import { debounce } from 'lodash-es'
const searchQuery = ref('')
// Debounced search function
const debouncedSearch = debounce(async (query: string) => {
if (query.trim()) {
await chatStore.searchChats(query)
} else {
await chatStore.fetchChats()
}
}, 300)
// Watch for search query changes
watch(searchQuery, (newQuery) => {
debouncedSearch(newQuery)
})
const openChat = (chat: Chat) => {
chatStore.setConversationActive(true)
// Navigate to chat or emit event
}
Data Transformation
The Chat Store transforms raw API data into the standardized Chat interface:
// Raw API Data Structure
const rawChatData = {
id: 1,
name: 'Alice Johnson',
status: 'online',
thumb: 'avatar1.jpg',
recent: true,
type: 'single',
email: 'alice@example.com',
username: '@alice_j',
lastSeen: new Date(),
chatHistory: [
{
id: 'msg_1_1',
senderId: 1,
msg: 'Hello!',
createdAt: new Date(),
type: 'text',
attachment: []
}
]
}
// Transformed Chat Object
const transformedChat = {
id: chatData.id,
name: chatData.name,
avatar: chatData.thumb,
lastMessage: chatData.chatHistory[chatData.chatHistory.length - 1]?.msg || '',
timestamp: chatData.chatHistory[chatData.chatHistory.length - 1]?.createdAt || new Date(),
unreadCount: Math.floor(Math.random() * 5), // Demo purposes
status: chatData.status,
isPinned: chatData.recent,
type: chatData.type,
email: chatData.email,
username: chatData.username,
lastSeen: chatData.lastSeen,
chatHistory: chatData.chatHistory
}
Error Handling
The Chat Store implements comprehensive error handling for robust user experience:
Error Handling Features
- Try-catch blocks for all async operations
- User-friendly error messages
- Error state reset on new operations
- Console logging for debugging
- Loading state management
Error Types
- Failed to fetch chats: Network or API errors
- Failed to search chats: Search operation errors
- Automatic error clearing on successful operations
Best Practices
Do
- Use computed properties for reactive data access
- Handle loading and error states in UI
- Debounce search operations
- Clear errors before new operations
- Use TypeScript for type safety
Avoid
- Directly modifying store state from components
- Ignoring error states in the UI
- Making API calls on every keystroke
- Forgetting to handle loading states
- Bypassing the store for chat data
Integration with Mock API
The Chat Store seamlessly integrates with the mock API system for development and testing:
// API Endpoint
const response = await axios.get('/api/data/chat/ChatData');
// Mock API Configuration (mockApis/chat/index.ts)
mock.onGet('/api/data/chat/ChatData').reply(() => {
return [200, ChatData]; // Returns static chat data
});
// Data includes:
// - 12 different chat conversations
// - Various message types (text, images, files, voice)
// - Different user statuses and types
// - Message history with timestamps
// - Attachment support