From d8c0a736d38c0667fadd4e04783de9d9485ae3ef Mon Sep 17 00:00:00 2001 From: susritha-s <88028321+susritha-s@users.noreply.github.com> Date: Sat, 8 Oct 2022 20:59:38 +0530 Subject: [PATCH 1/2] Add files via upload --- Chat Application/components/active-chat.js | 259 +++++++++ Chat Application/components/app-brand.js | 151 ++++++ Chat Application/components/authed-user.js | 264 +++++++++ Chat Application/components/chat-box.js | 511 ++++++++++++++++++ Chat Application/components/chat-list-item.js | 378 +++++++++++++ Chat Application/components/chat-message.js | 388 +++++++++++++ Chat Application/components/chats-list.js | 322 +++++++++++ Chat Application/components/component.js | 194 +++++++ Chat Application/components/new-message.js | 323 +++++++++++ Chat Application/index.html | 35 ++ Chat Application/scripts/chat-app.js | 207 +++++++ Chat Application/scripts/data-factory.js | 75 +++ Chat Application/scripts/index.js | 55 ++ Chat Application/scripts/recorder.js | 162 ++++++ Chat Application/static/chat-box-bg.png | Bin 0 -> 111249 bytes Chat Application/static/chat-placeholder.svg | 294 ++++++++++ Chat Application/static/mic.svg | 7 + Chat Application/styles/styles.css | 76 +++ 18 files changed, 3701 insertions(+) create mode 100644 Chat Application/components/active-chat.js create mode 100644 Chat Application/components/app-brand.js create mode 100644 Chat Application/components/authed-user.js create mode 100644 Chat Application/components/chat-box.js create mode 100644 Chat Application/components/chat-list-item.js create mode 100644 Chat Application/components/chat-message.js create mode 100644 Chat Application/components/chats-list.js create mode 100644 Chat Application/components/component.js create mode 100644 Chat Application/components/new-message.js create mode 100644 Chat Application/index.html create mode 100644 Chat Application/scripts/chat-app.js create mode 100644 Chat Application/scripts/data-factory.js create mode 100644 Chat Application/scripts/index.js create mode 100644 Chat Application/scripts/recorder.js create mode 100644 Chat Application/static/chat-box-bg.png create mode 100644 Chat Application/static/chat-placeholder.svg create mode 100644 Chat Application/static/mic.svg create mode 100644 Chat Application/styles/styles.css diff --git a/Chat Application/components/active-chat.js b/Chat Application/components/active-chat.js new file mode 100644 index 0000000..1ca9f02 --- /dev/null +++ b/Chat Application/components/active-chat.js @@ -0,0 +1,259 @@ +class ActiveChat extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return { + id: { + type: "string", + observe: true + }, + name: { + type: "string", + observe: true + }, + avatar: { + type: "string", + observe: true + }, + online: { + type: "boolean", + observe: true + }, + }; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(ActiveChat.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(ActiveChat.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: ActiveChat.attrTypes, + template: ActiveChat.template + }); + + this._backBtn = this.shadowRoot.getElementById("back-btn"); + + // render component + this.render(); + } + + // call on attributes changed + attributeChangedCallback(attrName, oldValue, newValue) { + if (oldValue === newValue) + return; + + // re-render component + this.render(); + } + + onMount() { + this._backBtn.addEventListener("click", this._onBackBtnClicked.bind(this)) + } + + onUnmount() { + this._backBtn.removeEventListener("click", this._onBackBtnClicked.bind(this)) + } + + /** + * fires when back btn clicked + * @private + */ + _onBackBtnClicked(){ + this.emit(APP_EVENTS.CHAT_BOX_BACK_CLICKED); + } + + /** + * render component according to template and attributes + */ + render() { + + // check the existence of avatar + // fetch first char of name to show if avatar not passed + if (!this.getAttribute("avatar")) { + // put first char of name when avatar not passed + const name = (this.getAttribute("name") || "").toUpperCase(); + this.shadowRoot.querySelector(".char-avatar").innerText = name.substr(0, 1); + } + + // loop over attributes and set all + for (let attr of this.attributes) { + const target = this.shadowRoot.getElementById(attr.name); + if (!target) + continue; + + switch (attr.name) { + case "name": + target.innerText = attr.value; + break; + case "avatar": + target.src = attr.value; + break; + } + + } + } + +} + +// define active-chat tag name +customElements.define(ActiveChat.tagName, ActiveChat); diff --git a/Chat Application/components/app-brand.js b/Chat Application/components/app-brand.js new file mode 100644 index 0000000..0237445 --- /dev/null +++ b/Chat Application/components/app-brand.js @@ -0,0 +1,151 @@ +class AppBrand extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return {}; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(AppBrand.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(AppBrand.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: AppBrand.attrTypes, + template: AppBrand.template + }); + + this._profileBtn = this.shadowRoot.getElementById("profile-btn"); + } + + // call on mounting + onMount() { + this.initListeners(); + } + + // call on un-mounting + onUnmount() { + this.removeListeners(); + } + + /** + * Initialize required listeners + */ + initListeners() { + this._profileBtn.addEventListener("click", this._onProfileBtnCLick.bind(this)) + } + + /** + * remove added listeners + */ + removeListeners() { + this._profileBtn.removeEventListener("click", this._onProfileBtnCLick.bind(this)) + } + + /** + * handle profile button click + * @param e + * @private + */ + _onProfileBtnCLick(e) { + this.emit(APP_EVENTS.PROFILE_BTN_CLICK) + } +} + +// define app-brand tag name +customElements.define(AppBrand.tagName, AppBrand); diff --git a/Chat Application/components/authed-user.js b/Chat Application/components/authed-user.js new file mode 100644 index 0000000..5cd0823 --- /dev/null +++ b/Chat Application/components/authed-user.js @@ -0,0 +1,264 @@ +class AuthedUser extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return { + id: { + type: "string", + observe: true + }, + name: { + type: "string", + observe: true + }, + avatar: { + type: "string", + observe: true + }, + hidden: { + type: "boolean", + observe: true + }, + }; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(AuthedUser.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(AuthedUser.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: AuthedUser.attrTypes, + template: AuthedUser.template + }); + + // render component + this.render(); + } + + // call on attributes changed + attributeChangedCallback(attrName, oldValue, newValue) { + if (oldValue === newValue) + return; + + // re-render component + this.render(); + } + + setUser(user) { + this._user = user; + + this.setAttribute("id", user.id); + this.setAttribute("name", user.name); + this.setAttribute("username", user.username); + this.setAttribute("avatar", user.avatar); + } + + /** + * reflect the hidden attr on HTML tag + * @param value + */ + set hidden(value) { + if (value) + this.setAttribute("hidden", ''); + else + this.removeAttribute("hidden") + } + + get hidden() { + return this.hasAttribute("hidden") + } + + /** + * render component according to template and attributes + */ + render() { + + // check the existence of avatar + // fetch first char of title to show if avatar not passed + if (!this.getAttribute("avatar")) { + // put first char of title when avatar not passed + const name = (this.getAttribute("name") || "").toUpperCase(); + this.shadowRoot.querySelector(".char-avatar").innerText = name.substr(0, 1); + } + + // loop over attributes and set all + for (let attr of this.attributes) { + const target = this.shadowRoot.getElementById(attr.name); + if (!target) + continue; + + switch (attr.name) { + case "username": + case "name": + target.innerText = attr.value; + break; + case "avatar": + target.src = attr.value; + break; + } + + } + } +} + +// define auth-user tag name +customElements.define(AuthedUser.tagName, AuthedUser); diff --git a/Chat Application/components/chat-box.js b/Chat Application/components/chat-box.js new file mode 100644 index 0000000..72e2acd --- /dev/null +++ b/Chat Application/components/chat-box.js @@ -0,0 +1,511 @@ +class ChatBox extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return { + hidden: { + type: "boolean", + observe: true + }, + }; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(ChatBox.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(ChatBox.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: ChatBox.attrTypes, + template: ChatBox.template + }); + + this._chatList = this.shadowRoot.getElementById("chat-list"); + this._newMessageBox = this.shadowRoot.querySelector("new-message"); + this._scrollToBottomBtn = this.shadowRoot.getElementById("scroll-to-bottom"); + this._activeChatElm = this.shadowRoot.querySelector("active-chat"); + } + + // call on mounting + onMount() { + this.initListeners(); + } + + // call on un-mounting + onUnmount() { + this.removeListeners(); + } + + // call on attributes changed + attributeChangedCallback(attrName, oldValue, newValue) { + if (oldValue === newValue) + return; + + if (attrName === "readonly") + this.checkNewMessageBoxVisibility(); + } + + /** + * reflect the readOnly attr on HTML tag + * @param value + */ + set readOnly(value) { + if (value) { + this.setAttribute('readonly', ''); + + } else { + this.removeAttribute('readonly'); + } + } + + get readOnly() { + return this.hasAttribute('readonly'); + } + + /** + * use this to set active chat of chatBox + * @param chat + */ + setActiveChat(chat) { + // if chat is not valid do nothing + if (!chat || !chat.id) + return; + + this._activeChat = chat; + + // make chatBox visibility visible anyway + // clear the chats lis + // and clear the lastMessage flag + this.hidden = false; + this._chatList.innerHTML = ''; + this.lastMessage = null; + + // render the chatBox header with activeChat + this.renderChatBoxHeader(); + } + + /** + * active chat getter + * @returns {*|{id}} + */ + get activeChat() { + return this._activeChat; + } + + /** + * reflect the hidden attr on HTML tag + * @param value + */ + set hidden(value) { + if (value) { + this.setAttribute("hidden", ''); + } else { + this.removeAttribute("hidden"); + } + } + + get hidden() { + return this.hasAttribute("hidden") + } + + /** + * Initialize required listeners + */ + initListeners() { + // listen for user sign-in + this.on(APP_EVENTS.USER_SIGN_IN, this._userSignIn.bind(this)); + // listen for new messages that send from authed user + this._newMessageBox.on(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, this._onAuthedMessageReceive.bind(this)); + + // control the visibility and the behavior of scrollToBottom button in chats list + this._chatList.addEventListener("scroll", this.checkScrollToBottomBtnVisibility.bind(this)); + this._scrollToBottomBtn.addEventListener("click", this.scrollToEnd.bind(this)); + + this._activeChatElm.on(APP_EVENTS.CHAT_BOX_BACK_CLICKED, this._onBackBtnClicked.bind(this)); + } + + /** + * remove added listeners + */ + removeListeners() { + this._newMessageBox.off(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, this._onAuthedMessageReceive.bind(this)); + this.off(APP_EVENTS.USER_SIGN_IN, this._userSignIn.bind(this)); + this._scrollToBottomBtn.removeEventListener("click", this.scrollToEnd.bind(this)); + this._chatList.removeEventListener("scroll", this.checkScrollToBottomBtnVisibility.bind(this)); + } + + /** + * Render message object and add to chats-box + * @param sender + * @param text + * @param audio + * @param time + * @param forceScrollToEnd + */ + renderMessage({sender, text, audio, time}, forceScrollToEnd = false) { + // if the message is invalid do nothing + if (!sender || (!text && !audio) || !time || !(time instanceof Date)) + return; + + const isFromAuthedUser = sender === this._authedUserId; + const isSameSender = this.lastMessage && this.lastMessage.sender === sender; + + // get time string to show in message bubble + const timeToShow = `${time.getHours()}:${time.getMinutes()}`; + + // create component element and set attributes + const msg = document.createElement("chat-message"); + + if (text) + msg.text = text; + + if (audio) + msg.audio = audio; + + msg.setAttribute("position", isFromAuthedUser ? "right" : "left"); + msg.setAttribute("sender", sender); + msg.setTimeObject(time); + msg.setAttribute("time", timeToShow); + msg.setAttribute("title", time.toLocaleString()); + msg.isLastInGroup = true; + + // check if the message is the last one that sender has been sent + // it's just for styling bubbles + if (this.lastMessage && isSameSender) + this.lastMessage.isLastInGroup = false; + + // render the day date and add to chat-box if needs + if (!this.lastMessage || this._isMessageForDifferentDay(time)) { + this._appendDateToChatList(time); + } + + // check if the user not scrolled top + // and the last message in chatBox is observable + const isLastMessageInView = this._chatList.scrollTop >= this._chatList.scrollHeight - this._chatList.clientHeight; + + // set lastMessage flat and append createdElement to chatList + this.lastMessage = msg; + this._chatList.appendChild(msg); + + // clear the newMessage input box if the sender is the authedUser + if (isFromAuthedUser) + this._newMessageBox.clear(); + + if (isLastMessageInView || forceScrollToEnd) + this.scrollToEnd(); + + // check the viability of scrollToBottom button + this.checkScrollToBottomBtnVisibility(); + } + + /** + * fire when a user signed in and set the this._authedUserId + * @param detail + * @private + */ + _userSignIn({detail}) { + this._authedUserId = detail.id; + } + + /** + * fires when back btn clicked in active-chat component + * @private + */ + _onBackBtnClicked() { + this.hidden = true; + + // send action to parent + this.emit(APP_EVENTS.CHAT_BOX_BACK_CLICKED) + } + + /** + * fire when a signed user send a message + * this will render the message and emit it to parent + * @param detail + * @private + */ + _onAuthedMessageReceive({detail}) { + if (!this.activeChat) + return; + + detail.toChat = this.activeChat.id; + detail.sender = this._authedUserId; + this.renderMessage(detail, true); + this.emit(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, detail); + } + + /** + * compare the time of the last received message with this._lastMessage flag + * @param time + * @returns {boolean} + * @private + */ + _isMessageForDifferentDay(time) { + if (!this.lastMessage || !time) + return false; + + return time.toDateString() !== this.lastMessage.timeObject.toDateString(); + } + + /** + * this generate the day title and a chat-day element put the time on element + * and append it to chat-list + * @param time + * @private + */ + _appendDateToChatList(time) { + const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; + this.assert(time, "Message time not passed"); + + let dayTitle = `${monthNames[time.getMonth()]} ${time.getDay()}, ${time.getFullYear()}`; + if (time.toDateString() === new Date().toDateString()) + dayTitle = "Today"; + + const dateNode = document.createElement("div"); + dateNode.classList.add("chat-day"); + dateNode.innerHTML = `${dayTitle}`; + + this._chatList.appendChild(dateNode); + } + + /** + * check the scroll height and scrollTop of chat-list + * and toggle the visibility of the scrollToBottom button + * @param e + */ + checkScrollToBottomBtnVisibility(e) { + if (this._chatList.scrollTop + 200 < this._chatList.scrollHeight - this._chatList.clientHeight) { + this._scrollToBottomBtn.classList.add("show") + } else { + this._scrollToBottomBtn.classList.remove("show"); + } + } + + /** + * scroll the chat list to the end + */ + scrollToEnd() { + this._chatList.scrollTo({ + top: this._chatList.scrollHeight, + }) + } + + /** + * render the activeChat component of the chatBox using this._activeChat + */ + renderChatBoxHeader() { + const activeChatNode = this.shadowRoot.querySelector("active-chat"); + this.assert(activeChatNode, "The active-chat node not found in chat-box"); + + activeChatNode.setAttribute("id", this._activeChat.id); + activeChatNode.setAttribute("name", this._activeChat.name); + activeChatNode.setAttribute("avatar", this._activeChat.avatar || ""); + if (this._activeChat.online) + activeChatNode.setAttribute("online", ''); + else + activeChatNode.removeAttribute("online"); + } + + /** + * check the new message box visibility + */ + checkNewMessageBoxVisibility() { + + // remove newMessageBox component if + // the chatBox is readOnly for logged in user + if (this.readOnly) { + this._newMessageBox.remove(); + } + + } + +} + +// define chat-box tag name +customElements.define(ChatBox.tagName, ChatBox); diff --git a/Chat Application/components/chat-list-item.js b/Chat Application/components/chat-list-item.js new file mode 100644 index 0000000..8d8caee --- /dev/null +++ b/Chat Application/components/chat-list-item.js @@ -0,0 +1,378 @@ +class ChatListItem extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return { + id: { + type: "string", + required: true, + }, + name: { + type: "string", + required: true, + observe: true + }, + desc: { + type: "string", + observe: true + }, + avatar: { + type: "string", + observe: true + }, + lastseen: { + type: "string", + observe: true + }, + unreadcount: { + type: "number", + observe: true + }, + online: { + type: "boolean", + observe: true + }, + }; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(ChatListItem.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(ChatListItem.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: ChatListItem.attrTypes, + template: ChatListItem.template + }); + + // render component + this.render(); + } + + // call on mounting + onMount() { + this.initListeners(); + } + + // call on un-mounting + onUnmount() { + this.removeListeners(); + } + + // call on attributes changed + attributeChangedCallback(attrName, oldValue, newValue) { + if (oldValue === newValue) + return; + + // re-render component + this.render(); + } + + /** + * reflect the selected attr on HTML tag + * @param value + */ + set selected(value) { + if (value) { + this.setAttribute('selected', ''); + + } else { + this.removeAttribute('selected'); + } + } + + get selected() { + return this.hasAttribute('selected'); + } + + // call on attributes changed + incrementUnreadCount() { + let count = 0; + if (this.getAttribute("unreadcount")) + count = parseInt(this.getAttribute("unreadcount")); + + this.setAttribute('unreadcount', count + 1); + } + + /** + * call this when you want to reset the unread messages count + */ + markAllAsRead() { + this.setAttribute('unreadcount', '0'); + } + + /** + * reflect the unread attr on HTML tag + * unread counter badge only shows on items that have truly unread attr + * @param value + */ + set unread(value) { + if (value) + this.setAttribute('unread', ''); + else + this.removeAttribute('unread'); + } + + get unread() { + return this.hasAttribute('unread'); + } + + /** + * reflect the online attr on HTML tag + * @param value + */ + set online(value) { + if (value) + this.setAttribute('online', ''); + else + this.removeAttribute('online'); + } + + get online() { + return this.hasAttribute('online'); + } + + /** + * Initialize required listeners + */ + initListeners() { + this.on("click", this._onClick) + } + + /** + * remove added listeners + */ + removeListeners() { + this.off("click", this._onClick) + } + + /** + * this method fire when chat-list-item clicked + * if the component is not disabled, it emit the + * clicked item id to above component (chatList) and + * set the selected attr + * @param e + * @private + */ + _onClick(e) { + e.preventDefault(); + if (this.disabled) { + return; + } + + // send selected chat-item id to parent component + this.emit(APP_EVENTS.CHAT_CLICKED, {id: this.getAttribute("id")}); + this.selected = true; + // reset unread counter of this chat + this.markAllAsRead(); + } + + /** + * render component according to template and attributes + */ + render() { + + // remove component if id not passed + if (!("id" in this.attributes)) { + this.remove() + } + + // check the existence of avatar + // fetch first char of name to show if avatar not passed + if (!this.getAttribute("avatar")) { + // put first char of name when avatar not passed + const name = (this.getAttribute("name") || "").toUpperCase(); + this.shadowRoot.querySelector(".char-avatar").innerText = name.substr(0, 1); + } + + // loop over attributes and set all + for (let attr of this.attributes) { + const target = this.shadowRoot.getElementById(attr.name); + if (target) + target.innerText = attr.value; + + switch (attr.name) { + case "avatar": + target.src = attr.value; + break; + + case "unreadcount": + this.unread = parseInt(attr.value) > 0; + break; + + } + + } + } + +} + +// define chat-list-item tag name +customElements.define(ChatListItem.tagName, ChatListItem); diff --git a/Chat Application/components/chat-message.js b/Chat Application/components/chat-message.js new file mode 100644 index 0000000..6f9e7d1 --- /dev/null +++ b/Chat Application/components/chat-message.js @@ -0,0 +1,388 @@ +class ChatMessage extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return { + sender: { + type: "string", + observe: true + }, + position: { + type: "string", + observe: true + }, + lastingroup: { + type: "boolean", + observe: true + }, + text: { + type: "string", + observe: true + }, + audio: { + type: "string", + observe: true + }, + time: { + type: "string", + observe: true + }, + }; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(ChatMessage.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(ChatMessage.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: ChatMessage.attrTypes, + template: ChatMessage.template + }); + + this._textElement = this.shadowRoot.getElementById("text"); + this._timeElement = this.shadowRoot.getElementById("time"); + + this._audioMessageCtrl = this.shadowRoot.querySelector(".audio-control"); + this._audioPlayBtn = this.shadowRoot.getElementById("audio-play"); + this._audioDurationElm = this.shadowRoot.getElementById("audio-duration"); + + } + + onMount() { + this._audioPlayBtn.addEventListener("click", this._onAudioPlayBtnClick.bind(this)) + } + + onUnmount() { + this._audioPlayBtn.removeEventListener("click", this._onAudioPlayBtnClick.bind(this)) + } + + // call on attributes changed + attributeChangedCallback(attrName, oldValue, newValue) { + if (oldValue === newValue) + return; + + // re-render component + this.render(); + } + + /** + * reflect the text attr on HTML tag + * @param value + */ + set text(value) { + if (value) { + this.setAttribute('text', value); + } else { + this.removeAttribute('text'); + } + } + + get text() { + return this.getAttribute('text'); + } + + /** + * reflect the audio attr on HTML tag + * @param audioObj + */ + set audio(audioObj) { + if (audioObj) { + + // set received object as _audio and do some process on it and + // add some listeners to control the play status or timing + this._audio = audioObj; + const {audio, duration, audioUrl} = audioObj; + this._audioElement = audio || new Audio(audioUrl); + + this._audioDurationElm.innerText = duration; + + const onPause = () => { + // handle icon changing for btn + this._audioPlayBtn.classList.remove("playing"); + }; + + const onEnded = () => { + // reset the duration text and handle icon changing for btn + this._audioDurationElm.innerText = duration; + this._audioPlayBtn.classList.remove("playing"); + }; + + const onTimeUpdate = () => { + // calc duration and update the text when audio is playing + const passedTime = Recorder.secToTimeStr(this._audioElement.currentTime); + this._audioDurationElm.innerText = `${passedTime} / ${duration}`; + }; + + const onPlay = () => { + this._audioPlayBtn.classList.add("playing"); + }; + this._audioElement.onended = onEnded; + this._audioElement.onpause = onPause; + this._audioElement.onplay = onPlay; + this._audioElement.ontimeupdate = onTimeUpdate; + + } else { + // set the _audio as null to remove it from component + this._audio = null; + } + } + + get audio() { + return this._audio; + } + + setTimeObject(value) { + this._timeObject = value; + } + + get timeObject() { + return this._timeObject; + } + + /** + * reflect the time attr on HTML tag + * @param value + */ + set time(value) { + if (value) { + this.setAttribute('time', value); + } else { + this.removeAttribute('time'); + } + } + + get time() { + return this.getAttribute('time'); + } + + /** + * reflect the sender attr on HTML tag + * @param value + */ + set sender(value) { + if (value) { + this.setAttribute('sender', value); + } else { + this.removeAttribute('sender'); + } + } + + get sender() { + return this.getAttribute('sender'); + } + + /** + * reflect the islastingroup attr on HTML tag + * @param value + */ + set isLastInGroup(value) { + if (value) { + this.setAttribute('lastingroup', ''); + } else { + this.removeAttribute('lastingroup'); + } + } + + get isLastInGroup() { + return this.hasAttribute('lastingroup'); + } + + /** + * fire when audio message play btn clicked and toggle the playing status + * @private + */ + _onAudioPlayBtnClick() { + if (!this._audioElement) + return; + + if (this._audioElement.paused) { + this._audioElement.play(); + } else { + this._audioElement.pause(); + } + } + + /** + * render message by attributes + */ + render() { + this._textElement.innerHTML = this.text; + this._timeElement.innerHTML = this.time; + this._audioMessageCtrl.style.display = this.audio ? "flex" : "none"; + } + +} + +// define chat-message tag name +customElements.define(ChatMessage.tagName, ChatMessage); diff --git a/Chat Application/components/chats-list.js b/Chat Application/components/chats-list.js new file mode 100644 index 0000000..4de2f12 --- /dev/null +++ b/Chat Application/components/chats-list.js @@ -0,0 +1,322 @@ +class ChatsList extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return {}; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(ChatsList.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(ChatsList.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: ChatsList.attrTypes, + template: ChatsList.template + }); + + this.chatsWrapper = this.shadowRoot.getElementById("chats-wrapper"); + this._searchInput = this.shadowRoot.getElementById("search-input"); + this._searchDebounceFlag = null; + this._selectedChat = null; + + } + + // call on mounting + onMount() { + this.initListeners(); + } + + // call on un-mounting + onUnmount() { + this.removeListeners(); + } + + /** + * Initialize required listeners + */ + initListeners() { + document.addEventListener("keydown", this._onKeyDown.bind(this)); + this._searchInput.addEventListener("input", this._onSearch.bind(this)); + this.on(APP_EVENTS.NEW_MESSAGE_RECEIVE, this._onNewMessageReceive.bind(this)); + this.on(APP_EVENTS.DESELECT_SELECTED_CHAT, this._onChatDeselect.bind(this)); + } + + /** + * remove added listeners + */ + removeListeners() { + document.removeEventListener("keydown", this._onKeyDown.bind(this)) + } + + /** + * Listen document keypress to handle Ctrl + F keys and focus on search box + * @param e + * @private + */ + _onKeyDown(e) { + if (e.ctrlKey && e.key === "f") { + e.preventDefault(); + this._searchInput.focus(); + } + } + + /** + * fires on search input change and call render on every 300ms + * @param e + * @private + */ + _onSearch(e) { + if (this._searchDebounceFlag) + clearTimeout(this._searchDebounceFlag); + + this._searchDebounceFlag = setTimeout(() => { + this.render(); + }, 300); + } + + /** + * fires when a new message received, and handle the + * position changing of chat that send/receive message + * also, handle the unread message count of the chat. + * @param detail + * @private + */ + _onNewMessageReceive({detail}) { + if (!detail.sender) + return; + + // actually here we find the target chat and + // remove it's element from chats-list DOM, + // update the unread messages count, + // generate a new element for it and append it + // as the first child of chats-list + const senderChatIndex = this._chats.findIndex(c => c.id === detail.sender); + const senderChat = this._chats.splice(senderChatIndex, 1)[0]; + + // update unread messages count + senderChat.unreadcount = (+senderChat.unreadcount || 0) + 1; + + // check the selection status of chat to apply it on the new element + const alreadySelected = senderChat.elm.selected; + + // remove previous element of chat + senderChat.elm.remove(); + + // generate new element for updated chat + senderChat.elm = this.generateChatListItem(senderChat); + + senderChat.elm.selected = alreadySelected; + + // push the updated chat to the start of the chats array + this._chats.unshift(senderChat); + + // append the updated chat to chatsList as the first child in the list + this.appendChatToList(senderChat, true); + } + + /** + * this method call from parent component to set the chats array + * every time, it makes the this.chatsWrapper empty and calls the render method + * @param chats + */ + setChats(chats) { + this._chats = chats; + this.chatsWrapper.innerHTML = ''; + this.render(); + } + + /** + * this method generate chat-list-item component for chat object + * @param chat + * @returns {HTMLElement} + */ + generateChatListItem(chat) { + const chatListItem = document.createElement("chat-list-item"); + chatListItem.setAttribute("id", chat.id); + chatListItem.setAttribute("title", chat.name); + chatListItem.setAttribute("name", chat.name); + chatListItem.setAttribute("avatar", chat.avatar); + chatListItem.setAttribute("desc", chat.desc || chat.username); + chatListItem.setAttribute("lastseen", chat.lastseen); + chatListItem.setAttribute("unreadcount", chat.unreadcount); + if (chat.online) + chatListItem.setAttribute("online", ''); + + // set the click listener of newly created component + chatListItem.on(APP_EVENTS.CHAT_CLICKED, this._onChatClicked.bind(this)); + + return chatListItem + } + + /** + * this method calls when component received a message to clear selection + * @private + */ + _onChatDeselect() { + if (!this._selectedChat) + return; + + this._selectedChat.elm.selected = false; + this._selectedChat = null; + + this.render(); + } + + /** + * fires when a chat-item-list has been clicked + * @param detail + * @private + */ + _onChatClicked({detail}) { + + this._selectedChat = null; + // we loop over chats to reset unread message counter + // of clicked chat and remove selection of other chats + this._chats.map(chat => { + if (chat.id !== detail.id) { + chat.elm.selected = false; + this._selectedChat = chat; + } else { + chat.unreadcount = 0; + } + }); + + // send the clicked chat-item details to parent component + this.emit(APP_EVENTS.CHAT_SELECTED, detail) + } + + /** + * this method controls the adding of chat to the this.chatsWrapper element + * also, the searching action happens here. it filters the chat with the value of search input + * @param chat + * @param appendFirst + */ + appendChatToList(chat, appendFirst = false) { + const searchTrend = (this._searchInput.value || '').toLowerCase(); + + // check if the chat.name or chat.username contains the value of search input or not + if (~chat.name.toLowerCase().indexOf(searchTrend) + || ~chat.username.toLowerCase().indexOf(searchTrend)) { + + if (!appendFirst) { // append at the end of the list + this.chatsWrapper.appendChild(chat.elm); + + } else { // append as the first child of the list + this.chatsWrapper.insertBefore(chat.elm, this.chatsWrapper.firstChild); + } + } + } + + /** + * render component according to template and attributes + */ + render() { + this.chatsWrapper.innerHTML = ''; + this._chats = this._chats.map(chat => { + + // generate chat-list-item component for the chat object + chat.elm = this.generateChatListItem(chat); + + // append chat to list + this.appendChatToList(chat); + + return chat + }) + } + +} + +// define chats-list tag name +customElements.define(ChatsList.tagName, ChatsList); diff --git a/Chat Application/components/component.js b/Chat Application/components/component.js new file mode 100644 index 0000000..8deb519 --- /dev/null +++ b/Chat Application/components/component.js @@ -0,0 +1,194 @@ +/** + * Main component class that other components extends from it + */ +class Component extends HTMLElement { + + constructor({attrTypes, template, shadowMode = "open"}) { + super(); + + this.attrTypes = attrTypes; + this._template = template; + this._shadowMode = shadowMode; + + // his method attach template to root if exists + this.makeShadow(); + } + + /** + * this method fire when component attached to DOM + */ + connectedCallback() { + // Check attributes types for each component + this.checkAttrs(); + // and call onMount method + // onMount is the only method that call inside connectedCallback + if (this.onMount && typeof this.onMount === "function") + this.onMount(); + } + + /** + * This method fire when component removed from DOM + */ + disconnectedCallback() { + // call onMount method of component. + // onUnmount is the only method that call inside disconnectedCallback + if (this.onUnmount && typeof this.onUnmount === "function") + this.onUnmount(); + } + + /** + * parse attribute types according to passed types + * @param value + * @param target + * @returns {(number | boolean | string)|*} + */ + parseAttrType(value, target) { + if (value === void 0 || value === null) + return value; + + switch (target) { + case "n": + case "number": + value = value.indexOf(".") ? parseFloat(value) : parseInt(value); + break; + + case "o": + case "object": + value = JSON.parse(value); + break; + + case "b": + case "bool": + case "boolean": + value = Boolean(value); + break; + + default: + value = value.toString() + } + + return value; + } + + /** + * check type of attributes + */ + checkAttrs() { + if (!this.attrTypes) + return; + + for (let [attr, details] of Object.entries(this.attrTypes)) { + + let value = this.parseAttrType(this.getAttribute(attr), details.type); + + // replace attribute with parsed value if value is not null + if (value !== null) + this.setAttribute(attr, value || ""); + + if (details.required) + this.assert(!!value, + `"${attr}" attr is knows as required but not passed to component.`); + + if (value !== null && details.type) { + this.assert(typeof value === details.type, + `The type of "${attr}" attr must be ${details.type}.`); + } + + } + } + + /** + * to check condition and fire event if its false + * @param condition + * @param error + */ + assert(condition, error) { + if (!condition) + console.error(`Warning: ${error}`) + } + + /** + * parse html and get content as html + * @returns {Node} + */ + parseTemplate() { + let parser = new DOMParser(); + const doc = parser.parseFromString(this._template, 'text/html'); + + return doc.querySelector("template").content.cloneNode(true); + } + + /** + * attach template to shadow + */ + makeShadow() { + // get template note + const template = this.parseTemplate(); + + // generate shadow dom + this.attachShadow({mode: this._shadowMode}).appendChild(template); + } + + /** + * dispatch an event + * @param event + * @param detail + */ + emit(event, detail) { + this.dispatchEvent(new CustomEvent(event, {detail})); + } + + /** + * Add listener to the host + * @param event + * @param callback + */ + on(event, callback) { + this.shadowRoot.host.addEventListener(event, callback.bind(this)) + } + + /** + * Remove listener of the host + * @param event + * @param callback + */ + off(event, callback) { + this.shadowRoot.host.removeEventListener(event, callback.bind(this)) + } + + get disabled() { + return this.hasAttribute('disabled'); + } + + /** + * reflect the disabled attr on HTML tag + * @param val + */ + set disabled(val) { + const isDisabled = Boolean(val); + if (isDisabled) + this.setAttribute('disabled', ''); + else + this.removeAttribute('disabled'); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static generateTagName(className) { + return className.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + } + + /** + * generate list of attrs has observe:true + * @param attrTypes {Object} + * @returns {string[]} + */ + static getObservedAttrs(attrTypes = {}) { + return Object.entries(attrTypes || {}) + .filter(([_, details]) => details.observe) + .map(([attr, _]) => attr); + } + +} diff --git a/Chat Application/components/new-message.js b/Chat Application/components/new-message.js new file mode 100644 index 0000000..2a43110 --- /dev/null +++ b/Chat Application/components/new-message.js @@ -0,0 +1,323 @@ +class NewMessage extends Component { + + /** + * define attributes types + * @returns {Object} + */ + static get attrTypes() { + return {}; + } + + /** + * generate observed attributes array from attr types object + */ + static get observedAttributes() { + return super.getObservedAttrs(NewMessage.attrTypes); + } + + /** + * generate tag-name from component class name + * @returns {string} + */ + static get tagName() { + return super.generateTagName(NewMessage.name); + } + + /** + * styles of component + * @returns {string} + */ + static get style() { + return (``) + } + + /** + * html template of component + * @returns {string} + */ + static get template() { + return (` + + `) + } + + constructor() { + super({ + attrTypes: NewMessage.attrTypes, + template: NewMessage.template + }); + + this._textSendButton = this.shadowRoot.getElementById("text-send-btn"); + this._soundRecordBtn = this.shadowRoot.getElementById("sound-record-btn"); + this._textarea = this.shadowRoot.getElementById("new-message-input"); + + // check if audio device is exists and make mic button visible + this.setMicBtnVisibility(Recorder.isMicAvailable()); + + // create recorder instance to control recording audio message + this._recorder = new Recorder(); + } + + /** + * toggle microphone button visibility + * @param showMic + */ + setMicBtnVisibility(showMic) { + this._soundRecordBtn.style.display = showMic ? "flex" : "none"; + this._textSendButton.style.display = showMic ? "none" : "flex"; + } + + // call on mounting + onMount() { + this.initListeners(); + } + + // call on un-mounting + onUnmount() { + this.removeListeners(); + } + + /** + * Initialize required listeners + */ + initListeners() { + this.addEventListener("keydown", this._onKeyPress.bind(this)); + this._textarea.addEventListener("input", this._onType.bind(this)); + this._textSendButton.addEventListener("click", this._onTextSend.bind(this)); + this._soundRecordBtn.addEventListener("mousedown", this._onRecordStart.bind(this)); + this._soundRecordBtn.addEventListener("touchstart", this._onRecordStart.bind(this)); + this._soundRecordBtn.addEventListener("mouseup", this._onRecordStop.bind(this)); + this._soundRecordBtn.addEventListener("touchend", this._onRecordStop.bind(this)); + } + + /** + * remove added listeners + */ + removeListeners() { + this.removeEventListener("keydown", this._onKeyPress.bind(this)); + this._textarea.removeEventListener("input", this._onType.bind(this)); + this._textSendButton.removeEventListener("click", this._onTextSend.bind(this)); + this._soundRecordBtn.removeEventListener("mousedown", this._onRecordStart.bind(this)); + this._soundRecordBtn.removeEventListener("touchstart", this._onRecordStart.bind(this)); + this._soundRecordBtn.removeEventListener("mouseup", this._onRecordStop.bind(this)); + this._soundRecordBtn.removeEventListener("touchend", this._onRecordStop.bind(this)); + + } + + /** + * fires when a key pressed and handle the Ctrl+Enter press + * if the user pressed Ctrl+Enter keys, this calls the send method + * @param e + * @private + */ + _onKeyPress(e) { + if (e.ctrlKey && e.key.toLowerCase() === "enter") { + this._onTextSend(); + } + } + + /** + * fires when the value of textarea changes, + * to toggle the visibility of text message sending button + * @param e + * @private + */ + _onType(e) { + this.setMicBtnVisibility(!e.target.value); + } + + /** + * fires when you want to send a new message, + * when the textarea has a valid value, this + * emit the message details to the parent component + * @private + */ + _onTextSend() { + + if (!this._textarea.value) { + this._textarea.focus(); + return; + } + + this.emit(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, { + text: this._textarea.value.trim(), + time: new Date(), + }) + } + + /** + * fires when mic btn pressed and hold, it means to start the recording + * @private + */ + _onRecordStart() { + // add "recording" class to record btn to start animation around the btn + this._soundRecordBtn.classList.add("recording"); + this._recorder.start(); + } + + /** + * fires when mic btn released, it means to stop the recording + * @returns {Promise} + * @private + */ + async _onRecordStop() { + // remove "recording" class to record btn to stop btn animation + this._soundRecordBtn.classList.remove("recording"); + + // generate the audioObj of recording and pass it as new message to parent component + let audio = await this._recorder.stop(); + this.emit(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, { + audio, + time: new Date(), + }) + } + + // getter for the value of textarea + get message() { + return this._textarea.value; + } + + /** + * this will clear the content of the textarea and puts focus on it + */ + clear() { + this._textarea.value = ""; + this._textarea.focus(); + + // check if audio device is exists and make mic button visible + this.setMicBtnVisibility(Recorder.isMicAvailable()) + } + +} + +customElements.define(NewMessage.tagName, NewMessage); diff --git a/Chat Application/index.html b/Chat Application/index.html new file mode 100644 index 0000000..b3ece45 --- /dev/null +++ b/Chat Application/index.html @@ -0,0 +1,35 @@ + + + + + + + + Web Chat App + + + +
+ + +
+ + + + + + + + + + + + + + + + diff --git a/Chat Application/scripts/chat-app.js b/Chat Application/scripts/chat-app.js new file mode 100644 index 0000000..ce702af --- /dev/null +++ b/Chat Application/scripts/chat-app.js @@ -0,0 +1,207 @@ +// connection between components is with events and +// this const object is for keeping all event types in one place to easy access +window.APP_EVENTS = { + PROFILE_BTN_CLICK: "profile-btn-click", + CHAT_CLICKED: "chat-clicked", + CHAT_SELECTED: "chat-selected", + AUTHED_USER_NEW_MESSAGE: "authed-user-new-message", + USER_SIGN_IN: "user-sign-in", + SEARCH_IN_CHATS: "search-in-chats", + NEW_MESSAGE_RECEIVE: "new-message-receive", + CHAT_BOX_BACK_CLICKED: "chat-box-back-clicked", + DESELECT_SELECTED_CHAT: "deselect-selected-chat", +}; + +/** + * This class controls the whole app + */ +class ChatApp { + + /** + * this receive the id of container of app and get it + * the container is required to run app + * @param appId + */ + constructor(appId) { + // check the existence of appId + this.assert(appId, "app container id not passed."); + this._app = document.getElementById(appId); + // check the existence of container + this.assert(this._app, `Container with id "${appId}" not found. `); + + this._authedUser = null; + this._chats = []; + this._messages = []; + this._componenets = {}; + + // find and assign required app-components + this.assignComponents(); + + // initialize listeners + this.initListeners(); + + // render the existed chats + this.sendChatsToList(); + } + + /** + * Find main components and assign it to this._components property + * It's just for remove duplication, after this, we access all components + * in this._components without re-select from DOM and just + */ + assignComponents() { + this._componenets.authedUser = document.querySelector("authed-user"); + this._componenets.appBranch = document.querySelector("app-brand"); + this._componenets.chatsList = document.querySelector("chats-list"); + this._componenets.chatBox = document.querySelector("chat-box"); + } + + /** + * This method is for initializing required events + */ + initListeners() { + this._componenets.appBranch.on(APP_EVENTS.PROFILE_BTN_CLICK, this._onProfileBtnClick.bind(this)); + this._componenets.chatsList.on(APP_EVENTS.CHAT_SELECTED, this._onChatSelected.bind(this)); + this._componenets.chatBox.on(APP_EVENTS.AUTHED_USER_NEW_MESSAGE, this._onAuthedUserNewMessages.bind(this)); + this._componenets.chatBox.on(APP_EVENTS.CHAT_BOX_BACK_CLICKED, this._onChatBoxBack.bind(this)); + } + + /** + * To simulate sign-in, use this method. + * Logged in user object should pass to this. + * @param user + */ + signin(user) { + // check the validity of user object + this.assert(user && user.id, "Invalid user object"); + this._authedUser = user; + + // after sign-in we need to set active user on authedUser component + // and make it hidden by default + // and tell the chatBox component that a user is signed-in + this._componenets.authedUser.setUser(this._authedUser); + this._componenets.authedUser.hidden = true; + this._componenets.chatBox.emit(APP_EVENTS.USER_SIGN_IN, {id: user.id}); + } + + /** + * use this method to send message to app + * @param msg {{time: Date, sender: String, text: String, toChat: String}} + */ + newMessage(msg) { + // check the validity of received msg object + this.assert(msg && msg.time && msg.sender && msg.text && msg.toChat, + `Invalid message object.`); + + // push to messages pool + this._messages.push(msg); + + // we need to check the sender of new message, if it send by logged in + // user we should tell chatBox to render the message too. + if (this.activeChat && msg.sender === this.activeChat.id) { + this._componenets.chatBox.renderMessage(msg); + } + // also we need to send received message to chatsList component + this._componenets.chatsList.emit(APP_EVENTS.NEW_MESSAGE_RECEIVE, msg); + } + + /** + * getter for this._authedUser + * @returns {Object} + */ + get authedUser() { + return this._authedUser; + } + + /** + * handle profile section visibility on profile-btn click + * @private + */ + _onProfileBtnClick() { + // toggle the visibility of authedUser component + this._componenets.authedUser.hidden = !this._componenets.authedUser.hidden + } + + /** + * this method fire when a chat selected. + * it find the messages of target chat and send those to chatBox to render + * @param detail + * @private + */ + _onChatSelected({detail}) { + // find all messages of selected chat + const chatMessaged = this._messages.filter(m => m.sender === detail.id || m.toChat === detail.id); + + // set selected chat as activeChat of whole app + this.activeChat = this._chats.find(c => c.id === detail.id); + + // if the chatBox if open for activeChat, scroll content to end + if (this._componenets.chatBox.activeChat && this._componenets.chatBox.activeChat.id === this.activeChat.id) { + this._componenets.chatBox.scrollToEnd(); + return; + } + + // mark all messages as read and remove unread badge for selected chat + this.activeChat.elm.markAllAsRead(); + // change the current chat of chatBox component + this._componenets.chatBox.setActiveChat(this.activeChat); + + // send all messages of target chat to render in chatBox + chatMessaged.map(msg => { + this._componenets.chatBox.renderMessage(msg) + }) + } + + /** + * this method fire when a new message from signed in user sent to a chat + * @param detail + * @private + */ + _onAuthedUserNewMessages({detail}) { + // add sender property to message and + // push it to the messages pool + this._messages.push({...detail, sender: this.authedUser.id}) + } + + /** + * fires when back btn clicked in chat-box + * @private + */ + _onChatBoxBack() { + this._componenets.chatsList.emit(APP_EVENTS.DESELECT_SELECTED_CHAT); + } + + /** + * send chats to chatList + */ + sendChatsToList() { + if (!this._chats) + return; + + this._componenets.chatsList.setChats(this._chats) + } + + /** + * use this method to add new chat to whole app + * @param chat + */ + addChat(chat) { + // check the validity of chat object + this.assert(chat && chat.id, `Invalid chat object.`); + + // update chats array + this._chats.push(chat); + + this.sendChatsToList(); + } + + /** + * to check condition and fire event if its false + * @param condition + * @param error + */ + assert(condition, error) { + if (!condition) + throw new Error(`${error}`) + } +} diff --git a/Chat Application/scripts/data-factory.js b/Chat Application/scripts/data-factory.js new file mode 100644 index 0000000..2f35423 --- /dev/null +++ b/Chat Application/scripts/data-factory.js @@ -0,0 +1,75 @@ +// generate random date +function randomDate() { + const start = 1585008324467; + const end = new Date().getTime(); + + let date = new Date(+start + Math.random() * (end - start)); + let hour = randomNumber(24, 1); + date.setHours(hour); + return date; +} + +// generate random number in range +function randomNumber(max, min = 0) { + return Math.floor(Math.random() * (max - min) + min); +} + +// generate chat object +function chatGenerator(index) { + const names = ["Mario Speedwagon", "Petey Cruiser", "Anna Sthesia", "Paul Molive", "Anna Mull", "Gail Forcewind", "Paige Turner", "Bob Frapples", "Walter Melon", "Nick R. Bocker", "Barb Ackue", "Buck Kinnear", "Greta Life", "Ira Membrit", "Shonda Leer", "Brock Lee", "Maya Didas", "Rick O'Shea", "Pete Sariya", "Monty Carlo", "Sal Monella", "Sue Vaneer", "Cliff Hanger", "Barb Dwyer", "Terry Aki", "Cory Ander", "Robin Banks", "Jimmy Changa", "Barry Wine", "Wilma Mumduya", "Buster Hyman", "Poppa Cherry", "Zack Lee", "Don Stairs", "Saul T. Balls", "Peter Pants", "Hal Appeno", "Otto Matic", "Moe Fugga", "Graham Cracker", "Tom Foolery", "Al Dente", "Bud Wiser", "Polly Tech", "Holly Graham", "Frank N. Stein", "Cam L. Toe", "Pat Agonia", "Tara Zona", "Barry Cade"] + const name = names[index]; + let lastseen = randomDate().toLocaleDateString().replace(/\//g, "."); + if (index % 3 === 0) + lastseen = "Today"; + if (index % 4 === 0) + lastseen = "Yesterday"; + + return { + id: Math.random().toString(32).substr(2, 10), + name, + username: name.replace(/[^a-zA-Z]/g, '').toLowerCase().substr(0, 8), + online: Math.random() > .7, + lastseen, + unreadcount: "0", + avatar: `https://randomuser.me/api/portraits/${index % 3 ? "women" : "men"}/${index + 1}.jpg`, + } +} + +// generate random sentences for messages +function getRandomText(sub = false) { + // sub-string a long paragraph. + if (sub) { + const lorem = `If the family member doesn’t need hospitalization and can be cared for at home, you should help him or her with basic needs and monitor the symptoms, while also keeping as much distance as possible, according to guidelines issued by the C.D.C. If there’s space, the sick family member should stay in a separate room and use a separate bathroom. If masks are available, both the sick person and the caregiver should wear them when the caregiver enters the room. Make sure not to share any dishes or other household items and to regularly clean surfaces like counters, doorknobs, toilets and tables. Don’t forget to wash your hands frequently.`; + const i1 = randomNumber(lorem.length, 6); + const i2 = randomNumber(lorem.length, 6); + const start = Math.min(i1, i2); + const end = Math.min(i2, i1); + return lorem.substr(start, end) + } + + // make a sentences of random words + let verbs, nouns, adjectives, adverbs, preposition; + nouns = ["bird", "clock", "boy", "plastic", "duck", "teacher", "old lady", "professor", "hamster", "dog", + "area", "book", "business", "case", "child", "company", "country", "day", "eye", + "fact", "family", "government", "group", "hand", "home", "job", "life", "lot"]; + verbs = ["kicked", "ran", "flew", "dodged", "sliced", "rolled", "died", "breathed", "slept", "killed", + "ask", "be", "become", "begin", "call", "can", "come", "could", "do", + "feel", "find", "get", "give", "go", "have", "hear", "help", "keep", "know",]; + adjectives = ["beautiful", "lazy", "professional", "lovely", "dumb", "rough", "soft", "hot", "vibrating", "slimy", "important", + "able", "bad", "best", "better", "big", "black", "certain", "clear", "different", "early", + "easy", "economic", "federal", "free", "full", "good", "great", "hard", "high", "human"]; + adverbs = ["slowly", "elegantly", "precisely", "quickly", "sadly", "humbly", "proudly", "shockingly", "calmly", "passionately"]; + preposition = ["down", "into", "up", "on", "upon", "below", "above", "through", "across", "towards"]; + + + var rand1 = Math.floor(Math.random() * 10); + var rand2 = Math.floor(Math.random() * 10); + var rand3 = Math.floor(Math.random() * 30); + var rand4 = Math.floor(Math.random() * 30); + var rand5 = Math.floor(Math.random() * 30); + var rand6 = Math.floor(Math.random() * 30); + return "The " + adjectives[rand1] + " " + nouns[rand2] + " " + adverbs[rand1] + " " + verbs[rand4] + " because some " + nouns[rand1] + + " " + adverbs[rand2] + " " + verbs[rand1] + " " + preposition[rand1] + " a " + adjectives[rand2] + " " + nouns[rand5] + + " which, became a " + adjectives[rand3] + ", " + adjectives[rand4] + " " + nouns[rand6] + "."; +} + diff --git a/Chat Application/scripts/index.js b/Chat Application/scripts/index.js new file mode 100644 index 0000000..28304e0 --- /dev/null +++ b/Chat Application/scripts/index.js @@ -0,0 +1,55 @@ +const numberOfChats = 10; +let fakeChats = []; +// generate an array of fake chats to show in app +for (let i = 1; i < numberOfChats; i++) { + fakeChats.push(chatGenerator(i)) +} + +// this is the signed-in user object +const authedUser = { + id: '12', + name: "Behnam Azimi", + username: "bhnmzm", + online: true, + lastSeen: "Today", + avatar: "https://randomuser.me/api/portraits/men/1.jpg" +}; + +// create instance of ChatApp, +// this is the line that run application +const app = new ChatApp("chat-web-app"); +app.signin(authedUser); + +// add all generated chats to app one-by-one +fakeChats.map(fc => app.addChat(fc)); + + +// below code is just for simulating message receive +// here we send 100 messages in different times ro app +let fakeMsgCounter = 100; +const interval = setInterval(() => { + + if (--fakeMsgCounter === 0) { + clearInterval(interval); + return; + } + + setTimeout(() => { + const fakeSender = fakeChats[randomNumber(numberOfChats, 1)]; + if (!fakeSender) + return; + + // flag with a 20% probability + const randomFlag = Math.random() > .8; + + app.newMessage({ + text: getRandomText(Math.random() > .5), + sender: randomFlag ? authedUser.id : fakeSender.id, + time: new Date(), + toChat: randomFlag ? fakeSender.id : authedUser.id + }); + + // new message sending time can be dynamic, between 1s and 5s + }, randomNumber(1000, 5000)) + +}, 1500); diff --git a/Chat Application/scripts/recorder.js b/Chat Application/scripts/recorder.js new file mode 100644 index 0000000..bcff52f --- /dev/null +++ b/Chat Application/scripts/recorder.js @@ -0,0 +1,162 @@ +/** + * this class control the recording functionality. + * it's enough to create an instance of this and call start() + * to start recording and stop() to put the end to the recording. + */ +class Recorder { + + constructor() { + this._recorder = null; + this._audioChunks = []; + + this.init(); + } + + /** + * initial the recorder and create a new instance of MediaRecorder. + */ + init() { + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + if (navigator.getUserMedia) { + navigator.getUserMedia({audio: true}, (stream) => { + + this._recorder = new MediaRecorder(stream); + + // we need to listen for data available for the recorder and update the audio chunk + this._recorder.addEventListener("dataavailable", (e) => { + this._audioChunks.push(e.data) + }); + + }, () => { + throw new Error("Use Media not found.") + }); + } else { + throw new Error("Use Media not found.") + } + } + + /** + * call start method of recorder + */ + start() { + if (!this._recorder) + return; + + this._audioChunks = []; + this._recorder.start(); + } + + /** + * call the stop method of recorder and generate the audio object and resolve it + * @returns {Promise} + */ + stop() { + return new Promise((resolve) => { + + // to create audio, we should listen for stop event of recorder + this._recorder.addEventListener("stop", async () => { + + // to create the audio, we should make its Blob first + // and then create a object URL for it and pass it to the Audio API + const audioBlob = new Blob(this._audioChunks); + const audioUrl = URL.createObjectURL(audioBlob); + this._audio = new Audio(audioUrl); + + // calc the duration of audio + const duration = await this.findDuration(audioBlob); + + resolve({audio: this._audio, duration, audioUrl}) + }); + + // stop the recording + this._recorder.stop(); + }) + } + + /** + * find duration of recorded audio + * @param blob + * @returns {Promise} + */ + findDuration(blob) { + return new Promise((resolve) => { + const file = new File([blob], "audio.mp3"); + let reader = new FileReader(); + reader.onload = (e) => { + let audioContext = new (window.AudioContext || window.webkitAudioContext)(); + + // Asynchronously decode audio file data contained in an ArrayBuffer. + audioContext.decodeAudioData(e.target.result, function (buffer) { + let floatDuration = buffer.duration; + + let dMin = Math.floor(floatDuration / 60); + let dSec = Math.floor(floatDuration % 60); + + if (dMin < 10) + dMin = "0" + dMin; + + if (dSec < 10) + dSec = "0" + dSec; + + resolve(`${dMin}:${dSec}`) + }); + }; + + reader.readAsArrayBuffer(file); + }) + } + + /** + * getter for audio objec + * @returns {HTMLAudioElement} + */ + get audio() { + return this._audio; + } + + /** + * check if the audio device is available + * @returns {boolean} + */ + static isMicAvailable() { + let isAvailable = true; + navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; + if (navigator.getUserMedia) { + navigator.getUserMedia({audio: true}, (stream) => { + isAvailable = true; + }, () => { + isAvailable = false; + }); + } + + return isAvailable; + } + + /** + * Convert numbers in second to time string like 00:00 + * + * @param seconds + * @returns {string} + */ + static secToTimeStr(seconds) { + let timeInHour = Math.floor(seconds / 3600); + let timeInMin = Math.floor((seconds % 3600) / 60); + let timeInSec = Math.floor(seconds % 60); + + if (timeInHour < 10) + timeInHour = `0${timeInHour}`; + + if (timeInMin < 10) + timeInMin = `0${timeInMin}`; + + if (timeInSec < 10) + timeInSec = `0${timeInSec}`; + + let timeStr = `${timeInMin}:${timeInSec}`; + if (parseInt(timeInHour)) + timeStr = `${timeInHour}:${timeStr}`; + + return timeStr; + } + +} diff --git a/Chat Application/static/chat-box-bg.png b/Chat Application/static/chat-box-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..a5eeab1e62184b8520247331dc532323a4069887 GIT binary patch literal 111249 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 literal 0 HcmV?d00001 diff --git a/Chat Application/static/chat-placeholder.svg b/Chat Application/static/chat-placeholder.svg new file mode 100644 index 0000000..e675709 --- /dev/null +++ b/Chat Application/static/chat-placeholder.svg @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Chat Application/static/mic.svg b/Chat Application/static/mic.svg new file mode 100644 index 0000000..06b79be --- /dev/null +++ b/Chat Application/static/mic.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/Chat Application/styles/styles.css b/Chat Application/styles/styles.css new file mode 100644 index 0000000..3195dca --- /dev/null +++ b/Chat Application/styles/styles.css @@ -0,0 +1,76 @@ +/** Simple Reset - START */ +html { + box-sizing: border-box; + font-size: 16px; + overflow-x: hidden; +} + +*, *:before, *:after { + box-sizing: inherit; +} + +body, h1, h2, h3, h4, h5, h6, p, ol, ul { + margin: 0; + padding: 0; + font-weight: normal; +} + +ol, ul { + list-style: none; +} + +img { + max-width: 100%; + height: auto; +} + +/** Global - START */ + +body { + font-family: 'Lato', sans-serif; + height: 100vh; + max-height: 100vh; + overflow: hidden; +} + +#chat-web-app { + position: fixed; + height: 100vh; + width: 100vw; + top: 0; + left: 0; + + display: flex; + flex-direction: row; +} + +#chat-web-app .sidebar { + box-shadow: 0 0 5px 2px rgba(0, 0, 0, .14); + display: flex; + flex-direction: column; + width: 280px; + min-width: 280px; + position: relative; + z-index: 2; +} + +@media screen and (max-width: 564px) { + #chat-web-app .sidebar { + min-width: 100%; + background-color: #fff; + z-index: 3; + } + + chat-box { + position: absolute; + top: 0; + left: 0; + z-index: 2; + background: #fff; + width: 100%; + } + + chat-box:not([hidden]) { + z-index: 5; + } +} From 433ba49359a426406442e0da4409de758de7daf8 Mon Sep 17 00:00:00 2001 From: susritha-s <88028321+susritha-s@users.noreply.github.com> Date: Sat, 8 Oct 2022 21:01:25 +0530 Subject: [PATCH 2/2] Create Readme.md --- Chat Application/Readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Chat Application/Readme.md diff --git a/Chat Application/Readme.md b/Chat Application/Readme.md new file mode 100644 index 0000000..05557a1 --- /dev/null +++ b/Chat Application/Readme.md @@ -0,0 +1 @@ +This is a frontend project for chat application