paint-brush
चैटप्लस का निर्माण: ओपन सोर्स PWA जो मोबाइल ऐप जैसा लगता हैद्वारा@aladinyo
1,148 रीडिंग
1,148 रीडिंग

चैटप्लस का निर्माण: ओपन सोर्स PWA जो मोबाइल ऐप जैसा लगता है

द्वारा Alaa Eddine Boubekeur29m2024/07/10
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

चैटप्लस मेरे द्वारा बनाए गए सबसे बेहतरीन अनुप्रयोगों में से एक है, यह अपने जीवंत उपयोगकर्ता इंटरफ़ेस और कई संदेश और कॉल कार्यात्मकताओं के साथ ध्यान आकर्षित करता है जो सभी वेब पर कार्यान्वित होते हैं, यह एक क्रॉस प्लेटफॉर्म अनुभव देता है क्योंकि इसका फ्रंटएंड एक PWA अनुप्रयोग के रूप में डिज़ाइन किया गया है जिसमें हर जगह इंस्टॉल करने और एक स्टैंडअलोन ऐप की तरह कार्य करने की क्षमता है, पुश नोटिफिकेशन जैसी इसकी सुविधा के साथ, चैटप्लस एक हल्के ऐप की परिभाषा है जो वेब प्रौद्योगिकियों के साथ मोबाइल ऐप की पूरी सुविधाएं दे सकता है।
featured image - चैटप्लस का निर्माण: ओपन सोर्स PWA जो मोबाइल ऐप जैसा लगता है
Alaa Eddine Boubekeur HackerNoon profile picture
0-item
1-item

चैटप्लस चैटिंग के लिए एक बेहतरीन PWA है 💬✨🤩

चैटप्लस एक प्रगतिशील वेब ऐप है जिसे रिएक्ट, नोडजेएस, फायरबेस और अन्य सेवाओं के साथ विकसित किया गया है।

आप अपने सभी दोस्तों से वास्तविक समय में बात कर सकते हैं 🗣️✨🧑‍🤝‍🧑❤️

आप अपने दोस्तों को कॉल कर सकते हैं और उनके साथ वीडियो और ऑडियो कॉल कर सकते हैं 🎥🔉🤩

अपने दोस्तों को चित्र और ऑडियो संदेश भेजें और आपके पास एक AI है जो आपके भाषण को पाठ में परिवर्तित करता है चाहे आप फ्रेंच, अंग्रेजी या स्पेनिश बोलते हों 🤖✨

वेब ऐप को किसी भी डिवाइस पर इंस्टॉल किया जा सकता है और नोटिफिकेशन प्राप्त किया जा सकता है ⬇️🔔🎉

मैं आपके समर्थन की बहुत सराहना करता हूँ, हमें गिटहब रिपॉजिटरी पर एक स्टार दें और अपने दोस्तों के साथ साझा करें ⭐✨

पूर्ण स्थापना और परिनियोजन दस्तावेज़ के लिए इस Github रिपॉजिटरी को देखें: https://github.com/aladinyo/ChatPlus

साफ़ सरल UI और UX

चैटप्लस मोबाइल दृश्य


चैटप्लस डेस्कटॉप दृश्य


तो चैटप्लस क्या है?

चैटप्लस मेरे द्वारा बनाए गए सबसे बेहतरीन अनुप्रयोगों में से एक है, यह अपने जीवंत उपयोगकर्ता इंटरफ़ेस और कई संदेश और कॉल कार्यात्मकताओं के साथ ध्यान आकर्षित करता है जो सभी वेब पर कार्यान्वित होते हैं, यह एक क्रॉस प्लेटफॉर्म अनुभव देता है क्योंकि इसका फ्रंटएंड एक PWA अनुप्रयोग के रूप में डिज़ाइन किया गया है जिसमें हर जगह इंस्टॉल करने और एक स्टैंडअलोन ऐप की तरह कार्य करने की क्षमता है, पुश नोटिफिकेशन जैसी इसकी सुविधा के साथ, चैटप्लस एक हल्के ऐप की परिभाषा है जो वेब प्रौद्योगिकियों के साथ मोबाइल ऐप की पूरी सुविधाएं दे सकता है।

सॉफ़्टवेयर वास्तुशिल्प

हमारा ऐप MVC सॉफ़्टवेयर आर्किटेक्चर का अनुसरण करता है, MVC (मॉडल-व्यू-कंट्रोलर) सॉफ़्टवेयर डिज़ाइन में एक पैटर्न है जिसका उपयोग आमतौर पर उपयोगकर्ता इंटरफ़ेस, डेटा और नियंत्रण तर्क को लागू करने के लिए किया जाता है। यह सॉफ़्टवेयर के व्यावसायिक तर्क और प्रदर्शन के बीच अलगाव पर जोर देता है। यह "चिंताओं का पृथक्करण" श्रम के बेहतर विभाजन और बेहतर रखरखाव के लिए प्रदान करता है।

उपयोगकर्ता इंटरफ़ेस परत (देखें)

इसमें कई रिएक्ट इंटरफ़ेस घटक शामिल हैं:

  • मटेरियल यूआई: मटेरियल यूआई एक ओपन-सोर्स रिएक्ट कंपोनेंट लाइब्रेरी है जो Google के मटेरियल डिज़ाइन को लागू करती है। यह व्यापक है और इसे बॉक्स से बाहर उत्पादन में इस्तेमाल किया जा सकता है, हम इसे आइकन बटन और ऑडियो स्लाइडर जैसे हमारे इंटरफ़ेस में कुछ तत्वों के लिए उपयोग करने जा रहे हैं।

  • लॉगिन दृश्य: एक सरल लॉगिन दृश्य जो उपयोगकर्ता को अपना उपयोगकर्ता नाम और पासवर्ड डालने या गूगल के साथ लॉगिन करने की अनुमति देता है।

  • साइडबार दृश्य : 'साइडबार__हेडर', 'साइडबार__सर्च', 'साइडबार मेनू' और 'साइडबारचैट्स' जैसे कई दृश्य घटक, इसमें उपयोगकर्ता जानकारी के लिए एक हेडर, उपयोगकर्ताओं को खोजने के लिए एक खोज बार, आपके चैट, आपके समूहों और उपयोगकर्ताओं के बीच नेविगेट करने के लिए एक साइडबार मेनू और आपके द्वारा हाल ही में भेजे गए सभी चैट को प्रदर्शित करने के लिए एक साइडबारचैट्स घटक शामिल है, यह उपयोगकर्ता का नाम और फोटो और अंतिम संदेश दिखाता है।

  • चैटव्यूज़: इसमें निम्नलिखित कई घटक शामिल हैं:

    1. 'चैट__हेडर' जिसमें आप जिस उपयोगकर्ता से बात कर रहे हैं उसकी जानकारी, उसकी ऑनलाइन स्थिति, प्रोफ़ाइल चित्र शामिल है और यह ऑडियो कॉल और वीडियो कॉल के लिए बटन प्रदर्शित करता है, यह यह भी प्रदर्शित करता है कि उपयोगकर्ता टाइप कर रहा है या नहीं।

    2. 'chat__body--container' इसमें अन्य उपयोगकर्ताओं के साथ हमारे संदेशों की जानकारी होती है, इसमें संदेशों का घटक होता है जो पाठ संदेश, उनके संदेश के साथ चित्र और ऑडियो संदेश भी प्रदर्शित करता है जिसमें उनकी जानकारी होती है जैसे ऑडियो समय और क्या ऑडियो चलाया गया था और इस घटक के अंत में हमारे पास 'seen' तत्व होता है जो प्रदर्शित करता है कि संदेश देखे गए थे या नहीं।

    3. 'ऑडियोप्लेयर': एक रिएक्ट घटक जो हमें नेविगेट करने के लिए एक स्लाइडर के साथ एक ऑडियो प्रदर्शित कर सकता है, ऑडियो का पूरा समय और वर्तमान समय प्रदर्शित करता है, यह दृश्य घटक 'chat__body-- कंटेनर' के अंदर लोड किया गया है।

    4. 'चैटफुटर': इसमें संदेश टाइप करने के लिए एक इनपुट, इनपुट पर टाइप करने पर संदेश भेजने के लिए एक बटन, अन्यथा बटन आपको ऑडियो रिकॉर्ड करने की अनुमति देगा, चित्र और फ़ाइलें आयात करने के लिए एक बटन।

    5. 'मीडियाप्रीव्यू': एक रिएक्ट घटक जो हमारे चैट में भेजने के लिए हमारे द्वारा चुनी गई छवियों या फ़ाइलों का पूर्वावलोकन करने की अनुमति देता है, वे एक हिंडोला पर प्रदर्शित होते हैं और उपयोगकर्ता छवियों या फ़ाइलों को स्लाइड कर सकता है और प्रत्येक के लिए एक विशिष्ट संदेश टाइप कर सकता है

    6. 'इमेजप्रीव्यू': जब हम अपनी चैट पर छवियां भेजते हैं तो यह घटक छवियों को पूर्ण स्क्रीन पर सुचारू एनीमेशन के साथ प्रदर्शित करेगा, घटक छवि पर क्लिक करने के बाद माउंट हो जाता है।

  • स्केलपेज: एक दृश्य फ़ंक्शन जो पूर्ण HD स्क्रीन और 4K स्क्रीन जैसी बड़ी स्क्रीन पर प्रदर्शित होने पर हमारे वेब ऐप के आकार को बढ़ाता है।

  • कॉलव्यूज़: प्रतिक्रिया घटकों का एक समूह जिसमें सभी कॉल दृश्य तत्व शामिल होते हैं, उनमें हमारी स्क्रीन पर खींचे जाने की क्षमता होती है और इसमें शामिल हैं:

    1 'बटन': एक कॉल बटन जिसका लाल संस्करण है तथा एक हरा वीडियो कॉल बटन।

    2 'ऑडियो कॉल व्यू': एक दृश्य घटक जो आने वाली ऑडियो कॉल का उत्तर देने और कॉल को टाइमर के साथ प्रदर्शित करने की अनुमति देता है और यह कॉल को रद्द करने की अनुमति देता है।

    3 'StartVideoCallView': एक दृश्य घटक जो स्थानीय मीडिया एपीआई से कनेक्ट करके हमारा वीडियो प्रदर्शित करता है और यह दूसरे उपयोगकर्ता द्वारा कॉल स्वीकार करने की प्रतीक्षा करता है या, यह आने वाली वीडियो कॉल का उत्तर देने के लिए हमारे लिए एक बटन प्रदर्शित करता है।

    4 'वीडियोकॉलव्यू': एक दृश्य घटक जो हमारा और दूसरे उपयोगकर्ता का वीडियो प्रदर्शित करता है, यह कैमरा बदलने, कैमरा और ऑडियो को अक्षम करने की अनुमति देता है, यह पूर्ण स्क्रीन पर भी जा सकता है।

  • रूट व्यू: रिएक्ट घटक जिसमें स्थानीय घटक नेविगेशन बनाने के लिए हमारे सभी दृश्य शामिल हैं, हमें 'वीडियोकॉल रूट', 'साइडबारमेनू रूट' और 'चैट्स रूट' मिला

क्लाइंट साइड मॉडल (मॉडल)

क्लाइंट साइड मॉडल वह तर्क है जो हमारे फ्रंटएंड को डेटाबेस और कई स्थानीय और सर्वरसाइड एपीआई के साथ बातचीत करने की अनुमति देता है और इसमें शामिल हैं:

  • फायरबेस SDK: यह एक SDK है जिसका उपयोग हमारे वेब ऐप का डेटाबेस बनाने के लिए किया जाता है।
  • ऐप मॉडल: एक मॉडल जो प्रमाणीकरण के बाद एक उपयोगकर्ता उत्पन्न करता है और यह भी सुनिश्चित करता है कि हमारे पास हमारी वेब परिसंपत्तियों का नवीनतम संस्करण है।
  • चैट मॉडल: इसमें डेटाबेस में संदेश भेजने, नए संदेशों को सुनने के लिए श्रोताओं की स्थापना, यह सुनना कि क्या दूसरा उपयोगकर्ता ऑनलाइन है और क्या वह टाइप कर रहा है, मॉडल लॉजिक शामिल हैं, यह हमारे मीडिया जैसे छवियों और ऑडियो को डेटाबेस स्टोरेज में भी भेजता है। साइडबार चैट मॉडल: लॉजिक जो उपयोगकर्ताओं के नवीनतम संदेशों को सुनता है और हमें उपयोगकर्ताओं से आपके सभी नए संदेशों की एक सरणी देता है, यह अपठित संदेशों की संख्या और उपयोगकर्ताओं की ऑनलाइन स्थिति भी देता है, यह अंतिम संदेश के समय के आधार पर उपयोगकर्ताओं को व्यवस्थित भी करता है।
  • उपयोगकर्ता खोज मॉडल: लॉजिक जो हमारे डेटाबेस पर उपयोगकर्ताओं की खोज करता है, यह एल्गोलिया खोज का उपयोग करता है जिसमें सर्वर पर हमारे डेटाबेस से लिंक करके हमारे उपयोगकर्ताओं की एक सूची होती है
  • कॉल मॉडल: लॉजिक जो हमारे वेब ऐप पर कॉल बनाने के लिए डेली SDK का उपयोग करता है और डेटा को हमारे सर्वर पर भेजता है और डेलीएपीआई के साथ इंटरैक्ट करता है।

क्लाइंट साइड नियंत्रक (नियंत्रक)

इसमें रिएक्ट घटक शामिल हैं जो हमारे दृश्यों को उनके विशिष्ट मॉडलों से जोड़ते हैं:

  • ऐप नियंत्रक: प्रमाणीकृत उपयोगकर्ता को सभी घटकों से जोड़ता है और हमारे ऐप के आकार को समायोजित करने के लिए स्केलपेज फ़ंक्शन चलाता है, यह फायरबेस को भी लोड करता है और सभी घटकों को जोड़ता है, हम इसे हमारे घटकों के लिए एक आवरण मान सकते हैं।
  • साइडबारकंट्रोलर: उपयोगकर्ताओं के डेटा और उनकी नवीनतम चैट की सूची को लिंक करता है, यह हमारे मेनू को उनके मॉडल लॉजिक के साथ भी जोड़ता है, यह सर्च बार को एल्गोलिया सर्च एपीआई के साथ भी जोड़ता है।
  • चैटकंट्रोलर: यह एक बहुत बड़ा नियंत्रक है जो अधिकांश संदेश और चैट सुविधाओं को जोड़ता है।
  • कॉलकंट्रोलर: कॉल मॉडल को उसके दृश्यों से जोड़ता है।

सर्वर साइड मॉडल

सभी सुविधाएं फ्रंटएंड पर नहीं की जाती हैं क्योंकि हमने जिन SDK का उपयोग किया है, उन्हें कुछ सर्वर साइड कार्यक्षमताओं की आवश्यकता होती है और वे इस प्रकार हैं:

  • कॉलसर्वरमॉडल: तर्क जो हमें डेली एपीआई के साथ बातचीत करके और हमारे फायरस्टोर डेटाबेस को अपडेट करके कॉल के लिए कमरे बनाने की अनुमति देता है।
  • ट्रांसक्रिप्ट मॉडल: सर्वर पर लॉजिक जो ऑडियो फ़ाइल प्राप्त करता है और Google क्लाउड स्पीच टू टेक्स्ट API के साथ इंटरैक्ट करता है और यह ऑडियो संदेशों के लिए एक ट्रांसक्रिप्ट देता है।
  • ऑनलाइन स्थिति हैंडलर: एक श्रोता जो उपयोगकर्ताओं की ऑनलाइन स्थिति को सुनता है और तदनुसार डेटाबेस को अपडेट करता है। अधिसूचना मॉडल: एक सेवा जो अन्य उपयोगकर्ताओं को अधिसूचना भेजती है।
  • एल्गोलियासेवर: एक श्रोता जो हमारे डेटाबेस पर नए उपयोगकर्ताओं को सुनता है और तदनुसार एल्गोलिया को अपडेट करता है ताकि हम इसे फ्रंटएंड पर खोज सुविधा के लिए उपयोग कर सकें।
  • सर्वर साइड नियंत्रक: कॉलसर्वर: एक एपीआई एंडपॉइंट जिसमें कॉलमॉडल शामिल है कार्यकर्ता: एक कार्यकर्ता सेवा जो हमारी सभी फायरबेस हैंडलिंग सेवाओं को चलाती है।

चैट फ्लो चार्ट

चैट फ्लो चार्ट


साइडबार फ्लो चार्ट

साइडबार फ्लो चार्ट

मॉडल कॉल फ्लो चार्ट:

मॉडल कॉल फ्लो चार्ट

कॉल फ़्लो चार्ट देखें

कॉल फ़्लो चार्ट देखें

बैकएंड वर्कर फ्लो चार्ट

बैकएंड वर्कर फ्लो चार्ट

डेटाबेस डिजाइन

हमारा वेब ऐप हमारे डेटाबेस को संग्रहीत करने के लिए फायरस्टोर का उपयोग करता है जो एक फायरबेस NoSql डेटाबेस है, हम उपयोगकर्ताओं की जानकारी संग्रहीत करते हैं, हम सभी संदेशों की एक सूची संग्रहीत करते हैं, हम चैट की सूची संग्रहीत करते हैं, और हम कमरों पर चैट भी संग्रहीत करते हैं, ये हमारे डेटाबेस पर संग्रहीत डेटा हैं:

  • प्रमाणीकरण के बाद उपयोगकर्ता डेटा.
  • कमरे जिनमें संदेशों का सारा विवरण होता है।
  • प्रत्येक उपयोगकर्ता के लिए नवीनतम चैट की सूची.
  • भेजी जाने वाली सूचनाओं की सूची.
  • ट्रांस्क्रिप्ट किये जाने वाले ऑडियो की सूची.

डेटाबेस यूएमएल

जादुई कोड की व्याख्या 🔮

अगले अध्यायों में मैं चैटप्लस में कुछ कार्यात्मकताओं के बारे में त्वरित व्याख्या और ट्यूटोरियल देने जा रहा हूं, मैं आपको जेएस कोड दिखाने जा रहा हूं और इसके पीछे के एल्गोरिदम की व्याख्या करूंगा और आपके कोड को डेटाबेस से जोड़ने के लिए सही एकीकरण उपकरण भी प्रदान करूंगा।

ऑनलाइन स्थिति को संभालना

उपयोगकर्ताओं की ऑनलाइन स्थिति को फ़ायरबेस डेटाबेस कनेक्टिविटी सुविधा का उपयोग करके फ्रंटएंड पर “.info/connected” से कनेक्ट करके और फ़ायरस्टोर और डेटाबेस दोनों को तदनुसार अपडेट करके लागू किया गया था:

 var disconnectRef; function setOnlineStatus(uid) { try { console.log("setting up online status"); const isOfflineForDatabase = { state: 'offline', last_changed: createTimestamp2, id: uid, }; const isOnlineForDatabase = { state: 'online', last_changed: createTimestamp2, id: uid }; const userStatusFirestoreRef = db.collection("users").doc(uid); const userStatusDatabaseRef = db2.ref('/status/' + uid); // Firestore uses a different server timestamp value, so we'll // create two more constants for Firestore state. const isOfflineForFirestore = { state: 'offline', last_changed: createTimestamp(), }; const isOnlineForFirestore = { state: 'online', last_changed: createTimestamp(), }; disconnectRef = db2.ref('.info/connected').on('value', function (snapshot) { console.log("listening to database connected info") if (snapshot.val() === false) { // Instead of simply returning, we'll also set Firestore's state // to 'offline'. This ensures that our Firestore cache is aware // of the switch to 'offline.' userStatusFirestoreRef.set(isOfflineForFirestore, { merge: true }); return; }; userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function () { userStatusDatabaseRef.set(isOnlineForDatabase); // We'll also add Firestore set here for when we come online. userStatusFirestoreRef.set(isOnlineForFirestore, { merge: true }); }); }); } catch (error) { console.log("error setting onlins status: ", error); } };

और हमारे बैकएंड पर हमने एक श्रोता भी स्थापित किया है जो हमारे डेटाबेस में परिवर्तनों को सुनता है और तदनुसार फायरस्टोर को अपडेट करता है, यह फ़ंक्शन हमें वास्तविक समय पर उपयोगकर्ता की ऑनलाइन स्थिति भी दे सकता है ताकि हम इसके अंदर अन्य फ़ंक्शन भी चला सकें:

 async function handleOnlineStatus(data, event) { try { console.log("setting online status with event: ", event); // Get the data written to Realtime Database const eventStatus = data.val(); // Then use other event data to create a reference to the // corresponding Firestore document. const userStatusFirestoreRef = db.doc(`users/${eventStatus.id}`); // It is likely that the Realtime Database change that triggered // this event has already been overwritten by a fast change in // online / offline status, so we'll re-read the current data // and compare the timestamps. const statusSnapshot = await data.ref.once('value'); const status = statusSnapshot.val(); // If the current timestamp for this data is newer than // the data that triggered this event, we exit this function. if (eventStatus.state === "online") { console.log("event status: ", eventStatus) console.log("status: ", status) } if (status.last_changed <= eventStatus.last_changed) { // Otherwise, we convert the last_changed field to a Date eventStatus.last_changed = new Date(eventStatus.last_changed); //handle the call delete handleCallDelete(eventStatus); // ... and write it to Firestore. await userStatusFirestoreRef.set(eventStatus, { merge: true }); console.log("user: " + eventStatus.id + " online status was succesfully updated with data: " + eventStatus.state); } else { console.log("next status timestamp is newer for user: ", eventStatus.id); } } catch (error) { console.log("handle online status crashed with error :", error) } }

सूचनाएं

नोटिफ़िकेशन एक बेहतरीन सुविधा है और इन्हें फ़ायरबेस मैसेजिंग का उपयोग करके कार्यान्वित किया जाता है, हमारे फ्रंटएंड पर यदि उपयोगकर्ता का ब्राउज़र नोटिफ़िकेशन का समर्थन करता है तो हम इसे कॉन्फ़िगर करते हैं और उपयोगकर्ता के फ़ायरबेस मैसेजिंग टोकन को पुनः प्राप्त करते हैं:

 const configureNotif = (docID) => { messaging.getToken().then((token) => { console.log(token); db.collection("users").doc(docID).set({ token: token }, { merge: true }) }).catch(e => { console.log(e.message); db.collection("users").doc(docID).set({ token: "" }, { merge: true }); }); }

जब भी कोई उपयोगकर्ता संदेश भेजता है, हम अपने डेटाबेस में एक अधिसूचना जोड़ते हैं:

 db.collection("notifications").add({ userID: user.uid, title: user.displayName, body: inputText, photoURL: user.photoURL, token: token, });


और हमारे बैकएंड पर हम नोटिफिकेशन संग्रह को सुनते हैं और हम इसे उपयोगकर्ता को भेजने के लिए फायरबेस मैसेजिंग का उपयोग करते हैं

 let listening = false; db.collection("notifications").onSnapshot(snap => { if (!listening) { console.log("listening for notifications..."); listening = true; } const docs = snap.docChanges(); if (docs.length > 0) { docs.forEach(async change => { if (change.type === "added") { const data = change.doc.data(); if (data) { const message = { data: data, token: data.token }; await db.collection("notifications").doc(change.doc.id).delete(); try { const response = await messaging.send(message); console.log("notification successfully sent :", data); } catch (error) { console.log("error sending notification ", error); }; }; }; }); }; });

एआई ऑडियो ट्रांसक्रिप्शन

हमारा वेब एप्लिकेशन उपयोगकर्ताओं को एक दूसरे को ऑडियो संदेश भेजने की अनुमति देता है, और इसकी एक विशेषता यह है कि इस ऑडियो को अंग्रेजी, फ्रेंच और स्पेनिश में रिकॉर्ड किए गए ऑडियो के लिए पाठ में परिवर्तित करने की क्षमता है और यह सुविधा Google क्लाउड स्पीच टू टेक्स्ट सुविधा के साथ लागू की गई थी, हमारा बैकएंड फायरस्टोर में जोड़े गए नए ट्रांसक्रिप्ट को सुनता है और उन्हें ट्रांसक्रिप्ट करता है और फिर उन्हें डेटाबेस में लिखता है:

 db.collection("transcripts").onSnapshot(snap => { const docs = snap.docChanges(); if (docs.length > 0) { docs.forEach(async change => { if (change.type === "added") { const data = change.doc.data(); if (data) { db.collection("transcripts").doc(change.doc.id).delete(); try { const text = await textToAudio(data.audioName, data.short, data.supportWebM); const roomRef = db.collection("rooms").doc(data.roomID).collection("messages").doc(data.messageID); db.runTransaction(async transaction => { const roomDoc = await transaction.get(roomRef); if (roomDoc.exists && !roomDoc.data()?.delete) { transaction.update(roomRef, { transcript: text }); console.log("transcript added with text: ", text); return; } else { console.log("room is deleted"); return; } }) db.collection("rooms").doc(data.roomID).collection("messages").doc(data.messageID).update({ transcript: text }); } catch (error) { console.log("error transcripting audio: ", error); }; }; }; }); }; });

जाहिर है, आपकी आंखें उस textToAudio फ़ंक्शन को देख रही होंगी और आप सोच रहे होंगे कि मैंने इसे कैसे बनाया, चिंता न करें मैं आपको बता देता हूं:

 // Imports the Google Cloud client library const speech = require('@google-cloud/speech').v1p1beta1; const { gcsUriLink } = require("./configKeys") // Creates a client const client = new speech.SpeechClient({ keyFilename: "./audio_transcript.json" }); async function textToAudio(audioName, isShort) { // The path to the remote LINEAR16 file const gcsUri = gcsUriLink + "/audios/" + audioName; // The audio file's encoding, sample rate in hertz, and BCP-47 language code const audio = { uri: gcsUri, }; const config = { encoding: "MP3", sampleRateHertz: 48000, languageCode: 'en-US', alternativeLanguageCodes: ['es-ES', 'fr-FR'] }; console.log("audio config: ", config); const request = { audio: audio, config: config, }; // Detects speech in the audio file if (isShort) { const [response] = await client.recognize(request); return response.results.map(result => result.alternatives[0].transcript).join('\n'); } const [operation] = await client.longRunningRecognize(request); const [response] = await operation.promise().catch(e => console.log("response promise error: ", e)); return response.results.map(result => result.alternatives[0].transcript).join('\n'); }; module.exports = textToAudio;

वीडियो कॉल सुविधा

हमारा वेब ऐप वास्तविक समय वेब आरटीसी कनेक्शन को लागू करने के लिए डेली एपीआई का उपयोग करता है, यह उपयोगकर्ताओं को एक दूसरे को वीडियो कॉल करने की अनुमति देता है इसलिए सबसे पहले हम एक बैकएंड कॉल सर्वर सेटअप करते हैं जिसमें डेली में कमरे बनाने और हटाने के लिए कई एपीआई प्रवेश बिंदु होते हैं:

 const app = express(); app.use(cors()); app.use(express.json()); app.delete("/delete-call", async (req, res) => { console.log("delete call data: ", req.body); deleteCallFromUser(req.body.id1); deleteCallFromUser(req.body.id2); try { fetch("https://api.daily.co/v1/rooms/" + req.body.roomName, { headers: { Authorization: `Bearer ${dailyApiKey}`, "Content-Type": "application/json" }, method: "DELETE" }); } catch(e) { console.log("error deleting room for call delete!!"); console.log(e); } res.status(200).send("delete-call success !!"); }); app.post("/create-room/:roomName", async (req, res) => { var room = await fetch("https://api.daily.co/v1/rooms/", { headers: { Authorization: `Bearer ${dailyApiKey}`, "Content-Type": "application/json" }, method: "POST", body: JSON.stringify({ name: req.params.roomName }) }); room = await room.json(); console.log(room); res.json(room); }); app.delete("/delete-room/:roomName", async (req, res) => { var deleteResponse = await fetch("https://api.daily.co/v1/rooms/" + req.params.roomName, { headers: { Authorization: `Bearer ${dailyApiKey}`, "Content-Type": "application/json" }, method: "DELETE" }); deleteResponse = await deleteResponse.json(); console.log(deleteResponse); res.json(deleteResponse); }) app.listen(process.env.PORT || 7000, () => { console.log("call server is running"); }); const deleteCallFromUser = userID => db.collection("users").doc(userID).collection("call").doc("call").delete();

और हमारे फ्रंटएंड पर हमें इस API को कॉल करने के लिए कई फ़ंक्शन मिले:

 import { callAPI as api } from "../../configKeys"; //const api = "http://localhost:7000" export async function createRoom(roomName) { var room = await fetch(`${api}/create-room/${roomName}`, { method: "POST", }); room = await room.json(); console.log(room); return room; } export async function deleteRoom(roomName) { var deletedRoom = await fetch(`${api}/delete-room/${roomName}`, { method: "DELETE", }); deletedRoom = await deletedRoom.json(); console.log(deletedRoom); console.log("deleted"); }; export function deleteCall() { window.callDelete && fetch(`${api}/delete-call`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify(window.callDelete) }); };

बढ़िया, अब समय आ गया है कॉल रूम बनाने और इन रूम से कनेक्ट करने तथा उनसे डेटा भेजने और प्राप्त करने के लिए दैनिक JS SDK का उपयोग करने का:

 export default async function startVideoCall(dispatch, receiverQuery, userQuery, id, otherID, userName, otherUserName, sendNotif, userPhoto, otherPhoto, audio) { var room = null; const call = new DailyIframe.createCallObject(); const roomName = nanoid(); window.callDelete = { id1: id, id2: otherID, roomName } dispatch({ type: "set_other_user_name", otherUserName }); console.log("audio: ", audio); if (audio) { dispatch({ type: "set_other_user_photo", photo: otherPhoto }); dispatch({ type: "set_call_type", callType: "audio" }); } else { dispatch({ type: "set_other_user_photo", photo: null }); dispatch({ type: "set_call_type", callType: "video" }); } dispatch({ type: "set_caller", caller: true }); dispatch({ type: "set_call", call }); dispatch({ type: "set_call_state", callState: "state_creating" }); try { room = await createRoom(roomName); console.log("created room: ", room); dispatch({ type: "set_call_room", callRoom: room }); } catch (error) { room = null; console.log('Error creating room', error); await call.destroy(); dispatch({ type: "set_call_room", callRoom: null }); dispatch({ type: "set_call", call: null }); dispatch({ type: "set_call_state", callState: "state_idle" }); window.callDelete = null; //destroy the call object; }; if (room) { dispatch({ type: "set_call_state", callState: "state_joining" }); dispatch({ type: "set_call_queries", callQueries: { userQuery, receiverQuery } }); try { await db.runTransaction(async transaction => { console.log("runing transaction"); var userData = (await transaction.get(receiverQuery)).data(); //console.log("user data: ", userData); if (!userData || !userData?.callerID || userData?.otherUserLeft) { console.log("runing set"); transaction.set(receiverQuery, { room, callType: audio ? "audio" : "video", isCaller: false, otherUserLeft: false, callerID: id, otherID, otherUserName: userName, otherUserRatio: window.screen.width / window.screen.height, photo: audio ? userPhoto : "" }); transaction.set(userQuery, { room, callType: audio ? "audio" : "video", isCaller: true, otherUserLeft: false, otherUserJoined: false, callerID: id, otherID }); } else { console.log('transaction failed'); throw userData; } }); if (sendNotif) { sendNotif(); const notifTimeout = setInterval(() => { sendNotif(); }, 1500); dispatch({ type: "set_notif_tiemout", notifTimeout }); } call.join({ url: room.url, videoSource: !audio }); } catch (userData) { //delete the room we made deleteRoom(roomName); await call.destroy(); if (userData.otherID === id) { console.log("you and the other user are calling each other at the same time"); joinCall(dispatch, receiverQuery, userQuery, userData.room, userName, audio ? userPhoto : "", userData.callType); } else { console.log("other user already in a call"); dispatch({ type: "set_call_room", callRoom: null }); dispatch({ type: "set_call", call: null }); dispatch({ type: "set_call_state", callState: "state_otherUser_calling" }); } }; }; };

OtherUserQuery और UserQuery सिर्फ फायरबेस फायरस्टोर दस्तावेज़ पथ हैं, अब ऐप के बाकी हिस्सों में दृश्य घटक हैं जो इस फ़ंक्शन द्वारा ट्रिगर किए गए राज्य परिवर्तनों पर प्रतिक्रिया करते हैं और हमारे कॉल यूआई तत्व तदनुसार दिखाई देंगे।

कॉल तत्व को इधर उधर ले जाएं:

यह अगला फ़ंक्शन मैजिक है जो आपको कॉल तत्व को पूरे पृष्ठ पर खींचने की अनुमति देता है:

 export function dragElement(elmnt, page) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0, top, left, prevTop = 0, prevLeft = 0, x, y, maxTop, maxLeft; const widthRatio = page.width / window.innerWidth; const heightRatio = page.height / window.innerHeight; //clear element's mouse listeners closeDragElement(); // setthe listener elmnt.addEventListener("mousedown", dragMouseDown); elmnt.addEventListener("touchstart", dragMouseDown, { passive: false }); function dragMouseDown(e) { e = e || window.event; // get the mouse cursor position at startup: if (e.type === "touchstart") { if (typeof(e.target.className) === "string") { if (!e.target.className.includes("btn")) { e.preventDefault(); } } else if (!typeof(e.target.className) === "function") { e.stopPropagation(); } pos3 = e.touches[0].clientX * widthRatio; pos4 = e.touches[0].clientY * heightRatio; } else { e.preventDefault(); pos3 = e.clientX * widthRatio; pos4 = e.clientY * heightRatio; }; maxTop = elmnt.offsetParent.offsetHeight - elmnt.offsetHeight; maxLeft = elmnt.offsetParent.offsetWidth - elmnt.offsetWidth; document.addEventListener("mouseup", closeDragElement); document.addEventListener("touchend", closeDragElement, { passive: false }); // call a function whenever the cursor moves: document.addEventListener("mousemove", elementDrag); document.addEventListener("touchmove", elementDrag, { passive: false }); } function elementDrag(e) { e = e || window.event; e.preventDefault(); // calculate the new cursor position: if (e.type === "touchmove") { x = e.touches[0].clientX * widthRatio; y = e.touches[0].clientY * heightRatio; } else { e.preventDefault(); x = e.clientX * widthRatio; y = e.clientY * heightRatio; }; pos1 = pos3 - x; pos2 = pos4 - y; pos3 = x pos4 = y; // set the element's new position: top = elmnt.offsetTop - pos2; left = elmnt.offsetLeft - pos1; //prevent the element from overflowing the viewport if (top >= 0 && top <= maxTop) { elmnt.style.top = top + "px"; } else if ((top > maxTop && pos4 < prevTop) || (top < 0 && pos4 > prevTop)) { elmnt.style.top = top + "px"; }; if (left >= 0 && left <= maxLeft) { elmnt.style.left = left + "px"; } else if ((left > maxLeft && pos3 < prevLeft) || (left < 0 && pos3 > prevLeft)) { elmnt.style.left = left + "px"; }; prevTop = y; prevLeft = x; } function closeDragElement() { // stop moving when mouse button is released: document.removeEventListener("mouseup", closeDragElement); document.removeEventListener("touchend", closeDragElement); document.removeEventListener("mousemove", elementDrag); document.removeEventListener("touchmove", elementDrag); }; return function() { elmnt.removeEventListener("mousedown", dragMouseDown); elmnt.removeEventListener("touchstart", dragMouseDown); closeDragElement(); }; };

छवियाँ खींचें और छोड़ें

आप अपनी चैट पर छवियों को खींचकर छोड़ सकते हैं और उन्हें दूसरे उपयोगकर्ता को भेज सकते हैं, यह कार्यक्षमता इसे चलाकर संभव हो जाती है:

 useEffect(() => { const dropArea = document.querySelector(".chat"); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation(); }, false); }); ['dragenter', 'dragover'].forEach(eventName => { dropArea.addEventListener(eventName, () => setShowDrag(true), false) }); ['dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, () => setShowDrag(false), false) }); dropArea.addEventListener('drop', e => { if (window.navigator.onLine) { if (e.dataTransfer?.files) { const dropedFile = e.dataTransfer.files; console.log("dropped file: ", dropedFile); const { imageFiles, imagesSrc } = mediaIndexer(dropedFile); setSRC(prevImages => [...prevImages, ...imagesSrc]); setImage(prevFiles => [...prevFiles, ...imageFiles]); setIsMedia("images_dropped"); }; }; }, false); }, []);

मीडियाइंडेक्सर एक सरल फ़ंक्शन है जो हमारे द्वारा प्रदान की गई छवियों के ब्लॉब को अनुक्रमित करता है:

 function mediaIndexer(files) { const imagesSrc = []; const filesArray = Array.from(files); filesArray.forEach((file, index) => { imagesSrc[index] = URL.createObjectURL(file); }); return { imagesSrc, imageFiles: filesArray }; }