diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f767596 Binary files /dev/null and b/.DS_Store differ diff --git a/icon192.png b/icon192.png new file mode 100644 index 0000000..cd035a3 Binary files /dev/null and b/icon192.png differ diff --git a/icon512.png b/icon512.png new file mode 100644 index 0000000..8f79164 Binary files /dev/null and b/icon512.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..7fdbe38 --- /dev/null +++ b/index.html @@ -0,0 +1,21 @@ + + + + + + + DMD 3440 - Room of Chat + + + + + +

Sending messages as {UserName}

+
+
+ + +
+ + + \ No newline at end of file diff --git a/main.css b/main.css new file mode 100644 index 0000000..0086299 --- /dev/null +++ b/main.css @@ -0,0 +1,47 @@ +* { + box-sizing: border-box; +} + +body { + background-color: rgb(32, 49, 88); + color:#fff; + font-family: Arial, Helvetica, sans-serif; +} + +#chatBox { + width:100%; + position:absolute; + bottom:70px; + top:60px; + right:0px; + left:0px; + background:#eee; + border:1px solid #333; + color:#000; + padding:15px; +} + +#chatInput { + position:fixed; + left:0px; + bottom:0; + width:100%; + background-color:#ccc; + display: flex; + justify-content: space-between; + height: 70px; + +} + +#chatInput input { + width:100%; + text-indent: 15px; +} + +#chatInput button { + margin:0px 0px; + padding:0px 10px; + background-color:#333; + color:#fff; + border:1px solid #333; +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..0d21f61 --- /dev/null +++ b/main.js @@ -0,0 +1,84 @@ +// Register a service worker, this one located in serviceworker.js +// A service worker is a piece of code the browser runs behind the scenes. +if ('serviceWorker' in navigator) { + console.log('CLIENT: service worker registration in progress.'); + navigator.serviceWorker.register('sw.js').then(function() { + console.log('CLIENT: service worker registration complete.'); + }, function() { + console.log('CLIENT: service worker registration failure.'); + }); + } else { + console.log('CLIENT: service workers are not supported.'); + } + + let allMessages = []; + let chatBox = document.querySelector("#chatBox"); + let newMsgInput = document.querySelector("#newMsg"); + let userName = localStorage.getItem('userName') || "me"; + let notifSound = 'notification.mp3'; + let notifSoundElement = new Audio(notifSound); + let usernameInput = document.querySelector("#usernameInput"); + usernameInput.innerHTML = userName; + + function clearChatInput() { + document.querySelector("#newMsg").value = ""; + document.querySelector("#newMsg").focus(); + } + + function displayNewMessage(msg) { + var newMsgP = document.createElement("p"); + newMsgP.innerHTML = "" + msg.sentBy + ": " + msg.message; + chatBox.appendChild(newMsgP); + + window.navigator.vibrate(200); + + notifSoundElement.play(); + clearChatInput(); + } + + function makeNewMessageFromLocalUser() { + let usrMsg = { + sentBy: userName, + dateStamp: Date.now(), + message: newMsgInput.value + } + + if(usrMsg.message !== "") { + allMessages.push(usrMsg); + // end make message + displayNewMessage(usrMsg) + } + } + + //** All the triggers and event listener assignments down here... */ + + // assign the "Make New Message" function to the button click + document.querySelector("#sendBtn").addEventListener('click', function(){ + makeNewMessageFromLocalUser() + }); + + // since we're not using a real form, assign it to the "keyup return" of the message input too + + document.querySelector("#newMsg").addEventListener('keyup', function(e) { + if (e.keyCode === 13) { + makeNewMessageFromLocalUser(); + } + }); + + // when username input is changed, update localstorage and global variable + usernameInput.addEventListener('blur', function() { + var oldUsername = localStorage.getItem("userName"); + var newUsername = this.innerHTML; + + var alertNew = { + sentBy: "system", + dateStamp: Date.now(), + message: `${oldUsername} has changed their name to ${newUsername}` + } + + displayNewMessage(alertNew); + + + localStorage.setItem("userName", newUsername); + userName = localStorage.getItem("userName"); + }); \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c98c45c --- /dev/null +++ b/manifest.json @@ -0,0 +1,21 @@ +{ + "short_name": "Room of Chat", + "name": "Room of Chat", + "icons": [ + { + "src":"icon192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icon512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "index.html", + "background_color": "#f00", + "theme_color": "#f00", + "display": "fullscreen" + } + \ No newline at end of file diff --git a/notification.mp3 b/notification.mp3 new file mode 100644 index 0000000..d8f9071 Binary files /dev/null and b/notification.mp3 differ diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..d2e3d44 --- /dev/null +++ b/sw.js @@ -0,0 +1,202 @@ + + +console.log('SERVICE WORKER: executing.'); + +/* A version number is useful when updating the worker logic, + allowing you to remove outdated cache entries during the update. +*/ +var version = 'v1::'; + +/* These resources will be downloaded and cached by the service worker + during the installation process. If any resource fails to be downloaded, + then the service worker won't be installed either. +*/ +var offlineFiles = [ + '', + 'icon192.png', + 'icon512.png', + 'index.html', + 'main.css', + 'main.js' +]; + +/* The install event fires when the service worker is first installed. + You can use this event to prepare the service worker to be able to serve + files while visitors are offline. +*/ +self.addEventListener("install", function(event) { + console.log('WORKER: install event in progress.'); + /* Using event.waitUntil(p) blocks the installation process on the provided + promise. If the promise is rejected, the service worker won't be installed. + */ + event.waitUntil( + /* The caches built-in is a promise-based API that helps you cache responses, + as well as finding and deleting them. + */ + caches + /* You can open a cache by name, and this method returns a promise. We use + a versioned cache name here so that we can remove old cache entries in + one fell swoop later, when phasing out an older service worker. + */ + .open(version + 'fundamentals') + .then(function(cache) { + /* After the cache is opened, we can fill it with the offline fundamentals. + The method below will add all resources in `offlineFiles` to the + cache, after making requests for them. + */ + return cache.addAll(offlineFiles); + }) + .then(function() { + console.log('WORKER: install completed'); + }) + ); +}); + +/* The fetch event fires whenever a page controlled by this service worker requests + a resource. This isn't limited to `fetch` or even XMLHttpRequest. Instead, it + comprehends even the request for the HTML page on first load, as well as JS and + CSS resources, fonts, any images, etc. +*/ +self.addEventListener("fetch", function(event) { + console.log('WORKER: fetch event in progress.'); + + /* We should only cache GET requests, and deal with the rest of method in the + client-side, by handling failed POST,PUT,PATCH,etc. requests. + */ + if (event.request.method !== 'GET') { + /* If we don't block the event as shown below, then the request will go to + the network as usual. + */ + console.log('WORKER: fetch event ignored.', event.request.method, event.request.url); + return; + } + /* Similar to event.waitUntil in that it blocks the fetch event on a promise. + Fulfillment result will be used as the response, and rejection will end in a + HTTP response indicating failure. + */ + event.respondWith( + caches + /* This method returns a promise that resolves to a cache entry matching + the request. Once the promise is settled, we can then provide a response + to the fetch request. + */ + .match(event.request) + .then(function(cached) { + /* Even if the response is in our cache, we go to the network as well. + This pattern is known for producing "eventually fresh" responses, + where we return cached responses immediately, and meanwhile pull + a network response and store that in the cache. + + Read more: + https://ponyfoo.com/articles/progressive-networking-serviceworker + */ + var networked = fetch(event.request) + // We handle the network request with success and failure scenarios. + .then(fetchedFromNetwork, unableToResolve) + // We should catch errors on the fetchedFromNetwork handler as well. + .catch(unableToResolve); + + /* We return the cached response immediately if there is one, and fall + back to waiting on the network as usual. + */ + console.log('WORKER: fetch event', cached ? '(cached)' : '(network)', event.request.url); + return cached || networked; + + function fetchedFromNetwork(response) { + /* We copy the response before replying to the network request. + This is the response that will be stored on the ServiceWorker cache. + */ + var cacheCopy = response.clone(); + + console.log('WORKER: fetch response from network.', event.request.url); + + caches + // We open a cache to store the response for this request. + .open(version + 'pages') + .then(function add(cache) { + /* We store the response for this request. It'll later become + available to caches.match(event.request) calls, when looking + for cached responses. + */ + return cache.put(event.request, cacheCopy); + }) + .then(function() { + console.log('WORKER: fetch response stored in cache.', event.request.url); + }); + + // Return the response so that the promise is settled in fulfillment. + return response; + } + + /* When this method is called, it means we were unable to produce a response + from either the cache or the network. This is our opportunity to produce + a meaningful response even when all else fails. It's the last chance, so + you probably want to display a "Service Unavailable" view or a generic + error response. + */ + function unableToResolve () { + /* There's a couple of things we can do here. + - Test the Accept header and then return one of the `offlineFiles` + e.g: `return caches.match('/some/cached/image.png')` + - You should also consider the origin. It's easier to decide what + "unavailable" means for requests against your origins than for requests + against a third party, such as an ad provider. + - Generate a Response programmaticaly, as shown below, and return that. + */ + + console.log('WORKER: fetch request failed in both cache and network.'); + + /* Here we're creating a response programmatically. The first parameter is the + response body, and the second one defines the options for the response. + */ + return new Response('

Service Unavailable

', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/html' + }) + }); + } + }) + ); +}); + +/* The activate event fires after a service worker has been successfully installed. + It is most useful when phasing out an older version of a service worker, as at + this point you know that the new worker was installed correctly. In this example, + we delete old caches that don't match the version in the worker we just finished + installing. +*/ +self.addEventListener("activate", function(event) { + /* Just like with the install event, event.waitUntil blocks activate on a promise. + Activation will fail unless the promise is fulfilled. + */ + console.log('WORKER: activate event in progress.'); + + event.waitUntil( + caches + /* This method returns a promise which will resolve to an array of available + cache keys. + */ + .keys() + .then(function (keys) { + // We return a promise that settles when all outdated caches are deleted. + return Promise.all( + keys + .filter(function (key) { + // Filter by keys that don't start with the latest version prefix. + return !key.startsWith(version); + }) + .map(function (key) { + /* Return a promise that's fulfilled + when each outdated cache is deleted. + */ + return caches.delete(key); + }) + ); + }) + .then(function() { + console.log('WORKER: activate completed.'); + }) + ); +}); \ No newline at end of file