'use client' import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react' // ============================================ // TYPES // ============================================ export interface Contact { id: string name: string email?: string phone?: string role: 'parent' | 'teacher' | 'staff' | 'student' student_name?: string class_name?: string notes?: string tags: string[] avatar_url?: string preferred_channel: 'email' | 'matrix' | 'pwa' online: boolean last_seen?: string created_at: string updated_at: string } export interface Message { id: string conversation_id: string sender_id: string // "self" for own messages content: string content_type: 'text' | 'file' | 'image' | 'voice' file_url?: string file_name?: string timestamp: string read: boolean read_at?: string delivered: boolean send_email: boolean email_sent: boolean email_sent_at?: string email_error?: string reply_to?: string // ID of message being replied to reactions?: { emoji: string; user_id: string }[] } export interface Conversation { id: string participant_ids: string[] group_id?: string created_at: string updated_at: string last_message?: string last_message_time?: string unread_count: number is_group: boolean title?: string typing?: boolean // Someone is typing pinned?: boolean muted?: boolean archived?: boolean } export interface MessageTemplate { id: string name: string content: string created_at: string } export interface MessagesStats { total_contacts: number total_conversations: number total_messages: number unread_messages: number } // ============================================ // CONTEXT INTERFACE // ============================================ interface MessagesContextType { // Data contacts: Contact[] conversations: Conversation[] messages: Record // conversationId -> messages templates: MessageTemplate[] stats: MessagesStats // Computed unreadCount: number recentConversations: Conversation[] // Actions fetchContacts: () => Promise fetchConversations: () => Promise fetchMessages: (conversationId: string) => Promise sendMessage: (conversationId: string, content: string, sendEmail?: boolean, replyTo?: string) => Promise markAsRead: (conversationId: string) => Promise createConversation: (contactId: string) => Promise addReaction: (messageId: string, emoji: string) => void deleteMessage: (conversationId: string, messageId: string) => void pinConversation: (conversationId: string) => void muteConversation: (conversationId: string) => void // State isLoading: boolean error: string | null currentConversationId: string | null setCurrentConversationId: (id: string | null) => void } const MessagesContext = createContext(null) // ============================================ // MOCK DATA - Realistic German school context // ============================================ const mockContacts: Contact[] = [ { id: 'contact_mueller', name: 'Familie Mueller', email: 'familie.mueller@gmail.com', phone: '+49 170 1234567', role: 'parent', student_name: 'Max Mueller', class_name: '10a', notes: 'Bevorzugt Kommunikation per E-Mail', tags: ['aktiv', 'Elternbeirat'], preferred_channel: 'email', online: false, last_seen: new Date(Date.now() - 1800000).toISOString(), created_at: new Date(Date.now() - 86400000 * 30).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_schmidt', name: 'Petra Schmidt', email: 'p.schmidt@web.de', phone: '+49 171 9876543', role: 'parent', student_name: 'Lisa Schmidt', class_name: '10a', tags: ['responsive'], preferred_channel: 'pwa', online: true, created_at: new Date(Date.now() - 86400000 * 60).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_weber', name: 'Sabine Weber', email: 's.weber@schule-musterstadt.de', role: 'teacher', tags: ['Fachschaft Deutsch', 'Klassenleitung 9b'], preferred_channel: 'pwa', online: true, last_seen: new Date().toISOString(), created_at: new Date(Date.now() - 86400000 * 90).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_hoffmann', name: 'Thomas Hoffmann', email: 't.hoffmann@schule-musterstadt.de', role: 'teacher', tags: ['Fachschaft Mathe', 'Oberstufenkoordinator'], preferred_channel: 'pwa', online: false, last_seen: new Date(Date.now() - 3600000 * 2).toISOString(), created_at: new Date(Date.now() - 86400000 * 120).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_becker', name: 'Familie Becker', email: 'becker.familie@gmx.de', phone: '+49 172 5551234', role: 'parent', student_name: 'Tim Becker', class_name: '10a', tags: [], preferred_channel: 'email', online: false, last_seen: new Date(Date.now() - 86400000).toISOString(), created_at: new Date(Date.now() - 86400000 * 45).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_klein', name: 'Monika Klein', email: 'm.klein@schule-musterstadt.de', role: 'staff', tags: ['Sekretariat'], preferred_channel: 'pwa', online: true, created_at: new Date(Date.now() - 86400000 * 180).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_fischer', name: 'Familie Fischer', email: 'fischer@t-online.de', phone: '+49 173 4445566', role: 'parent', student_name: 'Anna Fischer', class_name: '11b', tags: ['Foerderverein'], preferred_channel: 'pwa', online: false, last_seen: new Date(Date.now() - 7200000).toISOString(), created_at: new Date(Date.now() - 86400000 * 75).toISOString(), updated_at: new Date().toISOString() }, { id: 'contact_meyer', name: 'Dr. Hans Meyer', email: 'h.meyer@schule-musterstadt.de', role: 'teacher', tags: ['Schulleitung', 'Stellvertretender Schulleiter'], preferred_channel: 'email', online: false, last_seen: new Date(Date.now() - 3600000).toISOString(), created_at: new Date(Date.now() - 86400000 * 365).toISOString(), updated_at: new Date().toISOString() } ] const mockConversations: Conversation[] = [ { id: 'conv_mueller', participant_ids: ['contact_mueller'], created_at: new Date(Date.now() - 86400000 * 7).toISOString(), updated_at: new Date(Date.now() - 300000).toISOString(), last_message: 'Vielen Dank fuer die Info! Max freut sich schon auf die Klassenfahrt ๐ŸŽ‰', last_message_time: new Date(Date.now() - 300000).toISOString(), unread_count: 2, is_group: false, title: 'Familie Mueller', pinned: true }, { id: 'conv_schmidt', participant_ids: ['contact_schmidt'], created_at: new Date(Date.now() - 86400000 * 14).toISOString(), updated_at: new Date(Date.now() - 3600000).toISOString(), last_message: 'Lisa war heute krank, sie kommt morgen wieder.', last_message_time: new Date(Date.now() - 3600000).toISOString(), unread_count: 0, is_group: false, title: 'Petra Schmidt' }, { id: 'conv_weber', participant_ids: ['contact_weber'], created_at: new Date(Date.now() - 86400000 * 30).toISOString(), updated_at: new Date(Date.now() - 7200000).toISOString(), last_message: 'Koenntest du mir die Klausuraufgaben bis Freitag schicken? ๐Ÿ“', last_message_time: new Date(Date.now() - 7200000).toISOString(), unread_count: 1, is_group: false, title: 'Sabine Weber', typing: true }, { id: 'conv_hoffmann', participant_ids: ['contact_hoffmann'], created_at: new Date(Date.now() - 86400000 * 5).toISOString(), updated_at: new Date(Date.now() - 86400000).toISOString(), last_message: 'Die Notenkonferenz ist am 15.02. um 14:00 Uhr.', last_message_time: new Date(Date.now() - 86400000).toISOString(), unread_count: 0, is_group: false, title: 'Thomas Hoffmann' }, { id: 'conv_becker', participant_ids: ['contact_becker'], created_at: new Date(Date.now() - 86400000 * 3).toISOString(), updated_at: new Date(Date.now() - 172800000).toISOString(), last_message: 'Wir haben die Einverstaendniserklaerung unterschrieben.', last_message_time: new Date(Date.now() - 172800000).toISOString(), unread_count: 0, is_group: false, title: 'Familie Becker', muted: true }, { id: 'conv_fachschaft', participant_ids: ['contact_weber', 'contact_hoffmann', 'contact_meyer'], created_at: new Date(Date.now() - 86400000 * 60).toISOString(), updated_at: new Date(Date.now() - 14400000).toISOString(), last_message: 'Sabine: Hat jemand die neuen Lehrplaene schon gelesen?', last_message_time: new Date(Date.now() - 14400000).toISOString(), unread_count: 3, is_group: true, title: 'Fachschaft Deutsch ๐Ÿ“š' } ] const mockMessages: Record = { 'conv_mueller': [ { id: 'msg_m1', conversation_id: 'conv_mueller', sender_id: 'self', content: 'Guten Tag Frau Mueller,\n\nich moechte Sie ueber die anstehende Klassenfahrt nach Berlin informieren. Die Reise findet vom 15.-19. April statt.', content_type: 'text', timestamp: new Date(Date.now() - 86400000).toISOString(), read: true, delivered: true, send_email: true, email_sent: true, email_sent_at: new Date(Date.now() - 86400000).toISOString() }, { id: 'msg_m2', conversation_id: 'conv_mueller', sender_id: 'self', content: 'Die Kosten belaufen sich auf 280 Euro pro Schueler. Bitte ueberweisen Sie den Betrag bis zum 01.03. auf das Schulkonto.', content_type: 'text', timestamp: new Date(Date.now() - 86400000 + 60000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_m3', conversation_id: 'conv_mueller', sender_id: 'contact_mueller', content: 'Vielen Dank fuer die Information! Wir werden den Betrag diese Woche ueberweisen.', content_type: 'text', timestamp: new Date(Date.now() - 3600000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false, reactions: [{ emoji: '๐Ÿ‘', user_id: 'self' }] }, { id: 'msg_m4', conversation_id: 'conv_mueller', sender_id: 'contact_mueller', content: 'Vielen Dank fuer die Info! Max freut sich schon auf die Klassenfahrt ๐ŸŽ‰', content_type: 'text', timestamp: new Date(Date.now() - 300000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false } ], 'conv_schmidt': [ { id: 'msg_s1', conversation_id: 'conv_schmidt', sender_id: 'contact_schmidt', content: 'Guten Morgen! Lisa ist heute leider krank und kann nicht zur Schule kommen.', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 2).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_s2', conversation_id: 'conv_schmidt', sender_id: 'self', content: 'Gute Besserung an Lisa! ๐Ÿค’ Soll ich ihr die Hausaufgaben zukommen lassen?', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 2 + 1800000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_s3', conversation_id: 'conv_schmidt', sender_id: 'contact_schmidt', content: 'Das waere sehr nett, vielen Dank! ๐Ÿ™', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 2 + 3600000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_s4', conversation_id: 'conv_schmidt', sender_id: 'self', content: 'Hier sind die Hausaufgaben fuer diese Woche:\n\n๐Ÿ“– Deutsch: Seite 45-48 lesen\n๐Ÿ“ Mathe: Aufgaben 1-5 auf Seite 112\n๐Ÿ”ฌ Bio: Referat vorbereiten', content_type: 'text', timestamp: new Date(Date.now() - 86400000).toISOString(), read: true, delivered: true, send_email: true, email_sent: true }, { id: 'msg_s5', conversation_id: 'conv_schmidt', sender_id: 'contact_schmidt', content: 'Lisa war heute krank, sie kommt morgen wieder.', content_type: 'text', timestamp: new Date(Date.now() - 3600000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false } ], 'conv_weber': [ { id: 'msg_w1', conversation_id: 'conv_weber', sender_id: 'contact_weber', content: 'Hi! Hast du schon die neuen Abi-Themen gesehen?', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 3).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_w2', conversation_id: 'conv_weber', sender_id: 'self', content: 'Ja, habe ich! Finde ich ganz gut machbar dieses Jahr. ๐Ÿ“š', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 3 + 1800000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_w3', conversation_id: 'conv_weber', sender_id: 'contact_weber', content: 'Koenntest du mir die Klausuraufgaben bis Freitag schicken? ๐Ÿ“', content_type: 'text', timestamp: new Date(Date.now() - 7200000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false } ], 'conv_hoffmann': [ { id: 'msg_h1', conversation_id: 'conv_hoffmann', sender_id: 'contact_hoffmann', content: 'Kurze Info: Die Notenkonferenz ist am 15.02. um 14:00 Uhr.', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 2).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_h2', conversation_id: 'conv_hoffmann', sender_id: 'self', content: 'Danke fuer die Info! Bin dabei. ๐Ÿ‘', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 2 + 3600000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_h3', conversation_id: 'conv_hoffmann', sender_id: 'contact_hoffmann', content: 'Die Notenkonferenz ist am 15.02. um 14:00 Uhr.', content_type: 'text', timestamp: new Date(Date.now() - 86400000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false } ], 'conv_becker': [ { id: 'msg_b1', conversation_id: 'conv_becker', sender_id: 'self', content: 'Guten Tag Familie Becker,\n\nbitte vergessen Sie nicht, die Einverstaendniserklaerung fuer den Schwimmunterricht zu unterschreiben.', content_type: 'text', timestamp: new Date(Date.now() - 86400000 * 4).toISOString(), read: true, delivered: true, send_email: true, email_sent: true }, { id: 'msg_b2', conversation_id: 'conv_becker', sender_id: 'contact_becker', content: 'Wir haben die Einverstaendniserklaerung unterschrieben.', content_type: 'text', timestamp: new Date(Date.now() - 172800000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false } ], 'conv_fachschaft': [ { id: 'msg_f1', conversation_id: 'conv_fachschaft', sender_id: 'contact_meyer', content: 'Liebe Kolleginnen und Kollegen,\n\ndie neuen Lehrplaene sind jetzt online verfuegbar.', content_type: 'text', timestamp: new Date(Date.now() - 86400000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_f2', conversation_id: 'conv_fachschaft', sender_id: 'contact_hoffmann', content: 'Danke fuer die Info! Werde ich mir heute Abend anschauen.', content_type: 'text', timestamp: new Date(Date.now() - 72000000).toISOString(), read: true, delivered: true, send_email: false, email_sent: false }, { id: 'msg_f3', conversation_id: 'conv_fachschaft', sender_id: 'contact_weber', content: 'Hat jemand die neuen Lehrplaene schon gelesen?', content_type: 'text', timestamp: new Date(Date.now() - 14400000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false }, { id: 'msg_f4', conversation_id: 'conv_fachschaft', sender_id: 'contact_hoffmann', content: 'Noch nicht komplett, aber sieht interessant aus! ๐Ÿ“–', content_type: 'text', timestamp: new Date(Date.now() - 10800000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false }, { id: 'msg_f5', conversation_id: 'conv_fachschaft', sender_id: 'contact_meyer', content: 'Wir sollten naechste Woche eine Besprechung ansetzen.', content_type: 'text', timestamp: new Date(Date.now() - 7200000).toISOString(), read: false, delivered: true, send_email: false, email_sent: false } ] } const mockTemplates: MessageTemplate[] = [ { id: 'tpl_1', name: 'Krankmeldung bestaetigen', content: 'Vielen Dank fuer die Krankmeldung. Gute Besserung! ๐Ÿค’', created_at: new Date().toISOString() }, { id: 'tpl_2', name: 'Hausaufgaben senden', content: 'Hier sind die Hausaufgaben fuer diese Woche:\n\n๐Ÿ“– Deutsch: \n๐Ÿ“ Mathe: \n๐Ÿ”ฌ Bio: ', created_at: new Date().toISOString() }, { id: 'tpl_3', name: 'Elterngespraech anfragen', content: 'Guten Tag,\n\nich wuerde gerne ein Elterngespraech mit Ihnen vereinbaren. Wann haetten Sie Zeit?', created_at: new Date().toISOString() }, { id: 'tpl_4', name: 'Termin bestaetigen', content: 'Vielen Dank, der Termin ist bestaetigt. Ich freue mich auf unser Gespraech! ๐Ÿ“…', created_at: new Date().toISOString() } ] // ============================================ // PROVIDER // ============================================ export function MessagesProvider({ children }: { children: ReactNode }) { const [contacts, setContacts] = useState(mockContacts) const [conversations, setConversations] = useState(mockConversations) const [messages, setMessages] = useState>(mockMessages) const [templates, setTemplates] = useState(mockTemplates) const [stats, setStats] = useState({ total_contacts: mockContacts.length, total_conversations: mockConversations.length, total_messages: Object.values(mockMessages).flat().length, unread_messages: mockConversations.reduce((sum, c) => sum + c.unread_count, 0) }) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const [currentConversationId, setCurrentConversationId] = useState(null) const [mounted, setMounted] = useState(false) // Initialize useEffect(() => { setMounted(true) }, []) // Computed: unread count const unreadCount = conversations.reduce((sum, c) => sum + c.unread_count, 0) // Computed: recent conversations (sorted by last_message_time, pinned first) const recentConversations = [...conversations] .sort((a, b) => { // Pinned conversations first if (a.pinned && !b.pinned) return -1 if (!a.pinned && b.pinned) return 1 // Then by last_message_time const aTime = a.last_message_time ? new Date(a.last_message_time).getTime() : 0 const bTime = b.last_message_time ? new Date(b.last_message_time).getTime() : 0 return bTime - aTime }) // Actions const fetchContacts = useCallback(async () => { // Using mock data directly setContacts(mockContacts) }, []) const fetchConversations = useCallback(async () => { // Using mock data directly setConversations(mockConversations) }, []) const fetchMessages = useCallback(async (conversationId: string): Promise => { return messages[conversationId] || [] }, [messages]) const sendMessage = useCallback(async ( conversationId: string, content: string, sendEmail: boolean = false, replyTo?: string ): Promise => { const newMsg: Message = { id: `msg_${Date.now()}`, conversation_id: conversationId, sender_id: 'self', content, content_type: 'text', timestamp: new Date().toISOString(), read: true, delivered: true, send_email: sendEmail, email_sent: sendEmail, reply_to: replyTo } setMessages(prev => ({ ...prev, [conversationId]: [...(prev[conversationId] || []), newMsg] })) // Update conversation setConversations(prev => prev.map(c => c.id === conversationId ? { ...c, last_message: content.length > 50 ? content.slice(0, 50) + '...' : content, last_message_time: newMsg.timestamp, updated_at: newMsg.timestamp } : c )) return newMsg }, []) const markAsRead = useCallback(async (conversationId: string) => { setMessages(prev => ({ ...prev, [conversationId]: (prev[conversationId] || []).map(m => ({ ...m, read: true })) })) setConversations(prev => prev.map(c => c.id === conversationId ? { ...c, unread_count: 0 } : c )) }, []) const createConversation = useCallback(async (contactId: string): Promise => { // Check if conversation exists const existing = conversations.find(c => !c.is_group && c.participant_ids.includes(contactId) ) if (existing) return existing // Create new conversation const contact = contacts.find(c => c.id === contactId) const newConv: Conversation = { id: `conv_${Date.now()}`, participant_ids: [contactId], created_at: new Date().toISOString(), updated_at: new Date().toISOString(), unread_count: 0, is_group: false, title: contact?.name || 'Neue Konversation' } setConversations(prev => [newConv, ...prev]) setMessages(prev => ({ ...prev, [newConv.id]: [] })) return newConv }, [conversations, contacts]) const addReaction = useCallback((messageId: string, emoji: string) => { setMessages(prev => { const newMessages = { ...prev } for (const convId of Object.keys(newMessages)) { newMessages[convId] = newMessages[convId].map(msg => { if (msg.id === messageId) { const reactions = msg.reactions || [] const existingIndex = reactions.findIndex(r => r.user_id === 'self') if (existingIndex >= 0) { // Toggle or change reaction if (reactions[existingIndex].emoji === emoji) { reactions.splice(existingIndex, 1) } else { reactions[existingIndex].emoji = emoji } } else { reactions.push({ emoji, user_id: 'self' }) } return { ...msg, reactions } } return msg }) } return newMessages }) }, []) const deleteMessage = useCallback((conversationId: string, messageId: string) => { setMessages(prev => ({ ...prev, [conversationId]: (prev[conversationId] || []).filter(m => m.id !== messageId) })) }, []) const pinConversation = useCallback((conversationId: string) => { setConversations(prev => prev.map(c => c.id === conversationId ? { ...c, pinned: !c.pinned } : c )) }, []) const muteConversation = useCallback((conversationId: string) => { setConversations(prev => prev.map(c => c.id === conversationId ? { ...c, muted: !c.muted } : c )) }, []) // SSR safety if (!mounted) { return ( {}, fetchConversations: async () => {}, fetchMessages: async () => [], sendMessage: async () => null, markAsRead: async () => {}, createConversation: async () => null, addReaction: () => {}, deleteMessage: () => {}, pinConversation: () => {}, muteConversation: () => {}, isLoading: false, error: null, currentConversationId: null, setCurrentConversationId: () => {} }} > {children} ) } return ( {children} ) } export function useMessages() { const context = useContext(MessagesContext) if (!context) { throw new Error('useMessages must be used within a MessagesProvider') } return context } // ============================================ // HELPER FUNCTIONS // ============================================ export function formatMessageTime(timestamp: string): string { const date = new Date(timestamp) const now = new Date() const diffMs = now.getTime() - date.getTime() const diffMins = Math.floor(diffMs / 60000) const diffHours = Math.floor(diffMs / 3600000) const diffDays = Math.floor(diffMs / 86400000) if (diffMins < 1) return 'Gerade eben' if (diffMins < 60) return `${diffMins} Min.` if (diffHours < 24) return `${diffHours} Std.` if (diffDays === 1) return 'Gestern' if (diffDays < 7) return `${diffDays} Tage` return date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }) } export function formatMessageDate(timestamp: string): string { const date = new Date(timestamp) const now = new Date() const diffDays = Math.floor((now.getTime() - date.getTime()) / 86400000) if (diffDays === 0) return 'Heute' if (diffDays === 1) return 'Gestern' if (diffDays < 7) { return date.toLocaleDateString('de-DE', { weekday: 'long' }) } return date.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' }) } export function getContactInitials(name: string): string { const parts = name.split(' ').filter(p => p.length > 0) if (parts.length >= 2) { return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase() } return name.slice(0, 2).toUpperCase() } export function getRoleLabel(role: Contact['role']): string { const labels: Record = { parent: 'Eltern', teacher: 'Lehrkraft', staff: 'Verwaltung', student: 'Schueler/in' } return labels[role] || role } export function getRoleColor(role: Contact['role'], isDark: boolean): string { const colors: Record = { parent: { dark: 'bg-blue-500/20 text-blue-300', light: 'bg-blue-100 text-blue-700' }, teacher: { dark: 'bg-purple-500/20 text-purple-300', light: 'bg-purple-100 text-purple-700' }, staff: { dark: 'bg-amber-500/20 text-amber-300', light: 'bg-amber-100 text-amber-700' }, student: { dark: 'bg-green-500/20 text-green-300', light: 'bg-green-100 text-green-700' } } return isDark ? colors[role].dark : colors[role].light } // Emoji categories for picker export const emojiCategories = { 'Hรคufig': ['๐Ÿ‘', 'โค๏ธ', '๐Ÿ˜Š', '๐Ÿ˜‚', '๐Ÿ™', '๐Ÿ‘', '๐ŸŽ‰', 'โœ…', '๐Ÿ“', '๐Ÿ“š'], 'Smileys': ['๐Ÿ˜€', '๐Ÿ˜ƒ', '๐Ÿ˜„', '๐Ÿ˜', '๐Ÿ˜…', '๐Ÿ˜‚', '๐Ÿคฃ', '๐Ÿ˜Š', '๐Ÿ˜‡', '๐Ÿ™‚', '๐Ÿ˜‰', '๐Ÿ˜Œ', '๐Ÿ˜', '๐Ÿฅฐ', '๐Ÿ˜˜'], 'Gesten': ['๐Ÿ‘', '๐Ÿ‘Ž', '๐Ÿ‘Œ', 'โœŒ๏ธ', '๐Ÿคž', '๐Ÿค', '๐Ÿ‘', '๐Ÿ™Œ', '๐Ÿ‘‹', 'โœ‹', '๐Ÿคš', '๐Ÿ–๏ธ', '๐Ÿ™'], 'Symbole': ['โค๏ธ', '๐Ÿ’™', '๐Ÿ’š', '๐Ÿ’›', '๐Ÿงก', '๐Ÿ’œ', 'โœ…', 'โŒ', 'โญ', '๐ŸŒŸ', '๐Ÿ’ฏ', '๐Ÿ“Œ', '๐Ÿ“Ž'], 'Schule': ['๐Ÿ“š', '๐Ÿ“–', '๐Ÿ“', 'โœ๏ธ', '๐Ÿ““', '๐Ÿ“•', '๐Ÿ“—', '๐Ÿ“˜', '๐ŸŽ“', '๐Ÿซ', '๐Ÿ“…', 'โฐ', '๐Ÿ””'] }