diff --git a/public/css/style.css b/public/css/style.css
index 709c565..44a1757 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -135,7 +135,7 @@ form {
text-decoration: none;
color: inherit;
display: inline-block;
- width: 100px;
+ /* width: 100px; */
overflow: hidden;
text-overflow: ellipsis;
}
diff --git a/public/js/index.js b/public/js/index.js
index 05c1199..20d4043 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -3,60 +3,81 @@ const table_body = document.querySelector('#tbody');
const clipsListContainer = document.querySelector('#clips-list-container');
const err_msg = document.querySelector('#msg');
const clipText = " \n Amazingly shortened with Trim. Visit http://trim.ng to trim your Links!!!";
+const syncDevicesForm = document.querySelector('#sync-devices-form');
+const showSyncID = document.querySelector('.show-sync-id')
+
+const generateClipRow = (shortenedUrl) => {
+ let expiry
+ if (shortenedUrl.expiry_date) {
+ expiry = new Date(shortenedUrl.expiry_date).toDateString()
+ }
+ return `
+
+
+ ${shortenedUrl.click_count}
+
+ |
+
+
+ ${shortenedUrl.clipped_url}
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+ ${shortenedUrl.long_url}
+ |
+
+ ${expiry || '—' }
+ |
+ `
+}
/**Gets the new trim returned from the server and adds it to the display.
* Prints an error if the server returns an error message.
* @param {Response} response. The response object.
*/
-const printNewTrim = async(response)=> {
+const renderShorts = async(response)=> {
if (!response.ok)
return showError(response, true);
-
trimUrlForm.reset();
- let tr_clip = document.createElement('tr')
- tr_clip.id = 'table-body'
- // Logic to add new trim to the list here.
try {
- const newClip = await response.json()
- let { _id, click_count, long_url, urlCode, clipped_url, expiry_date} = await newClip.payload;
-
- if (expiry_date)
- expiry_date = new Date(expiry_date).toDateString();
-
- const clip_row = `
-
-
- ${click_count}
-
- |
-
-
- ${clipped_url}
-
- |
-
-
-
-
-
-
-
-
-
-
- |
-
- ${long_url}
- |
-
- ${expiry_date || '—'}
- |
- `
-
- tr_clip.innerHTML = clip_row
- clipsListContainer.style.display = "initial";
-
- table_body.prepend(tr_clip)
+ const clips = await response.json()
+ if (clips.userClips) {
+ const { userClips } = clips
+ for (let shortUrl of userClips) {
+ const tr_clip = document.createElement('tr')
+ tr_clip.id = 'table-body'
+ tr_clip.innerHTML = generateClipRow(shortUrl)
+ table_body.append(tr_clip)
+ }
+ syncDevicesForm.reset()
+ showSyncID.innerHTML = `Here is your syncID: ${clips.created_by}`
+ } else if (clips.payload) {
+ // Logic to add new trim to the list here.
+ const { payload } = clips
+ const tr_clip = document.createElement('tr')
+ tr_clip.id = 'table-body'
+ tr_clip.innerHTML = generateClipRow(payload)
+ clipsListContainer.style.display = "initial";
+ table_body.prepend(tr_clip)
+ showSyncID.innerHTML = `Here is your syncID: ${payload.created_by}`
+ }
+ if(syncDevicesForm){
+ syncDevicesForm.style.display = 'none'
+ }
+ showSyncID.style.display = 'block'
}
//Handle browser error here.
catch(error) {
@@ -104,7 +125,23 @@ if(trimUrlForm){
},
body: JSON.stringify({...urlData})
})
- .then(printNewTrim) //Be sure to handle error response from the server.
+ .then(renderShorts) //Be sure to handle error response from the server.
.catch(showError); //If the browser fails to communicate with the server, handle such errors here.
}
+}
+
+if (syncDevicesForm) {
+ syncDevicesForm.onsubmit = (e) => {
+ e.preventDefault()
+ const userID = document.querySelector("input[name='userID'")
+ fetch('/sync-device', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({userID: userID.value})
+ })
+ .then(renderShorts)
+ .catch(showError)
+ }
}
\ No newline at end of file
diff --git a/src/controllers/urlController.js b/src/controllers/urlController.js
index c19d786..7aa88a1 100644
--- a/src/controllers/urlController.js
+++ b/src/controllers/urlController.js
@@ -83,3 +83,24 @@ export const getUrlAndUpdateCount = async (req, res, next) => {
return res.status(404).render('error');
}
};
+
+
+/**
+ * This function gets original url by the trim code supplied as a parameter
+ * e.g trim.ly/TRIM_CODE
+ * @param {object} req
+ * @param {object} res
+ * @returns {object} next middleware
+ */
+export const deleteUrl = async (req, res, next) => {
+ try {
+ const { urlId } = req.body;
+ if (!urlId) {
+ return next();
+ }
+ await UrlShorten.findByIdAndDelete(urlId);
+ return res.status(200).json({ success: true })
+ } catch (error) {
+ return res.status(404).render('error');
+ }
+};
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 90026c5..a2291d7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,7 +8,6 @@ const { initRoutes } = require('./routes/routes');
const db = require('./database/db');
const app = express();
-
app.use((req, res, next) => {
//res.setHeader('Access-Control-Allow-Origin', '*'); //Don't think we need CORS here.
res.setHeader(
diff --git a/src/middlewares/getSyncData.js b/src/middlewares/getSyncData.js
new file mode 100644
index 0000000..c67a929
--- /dev/null
+++ b/src/middlewares/getSyncData.js
@@ -0,0 +1,27 @@
+import UrlShorten from "../models/UrlShorten";
+/**
+ * This function renders the landing page and gets list of user trimmed urls
+ * @param {object} req
+ * @param {object} res
+ * @returns {object} response object with trimmed urls
+ */
+export const getSyncedData = (req, res) => {
+ const { userID } = req.body;
+ res.cookie("userID", userID, {
+ maxAge: 1000 * 60 * 60 * 60 * 30,
+ });
+ UrlShorten.find({
+ created_by: userID //Find all clips created by this user.
+ })
+ .sort({
+ createdAt: "desc" // sort the created clips in a decending order
+ })
+ .then(clips => {
+ //Pass the user's clips to the view engine to render the customized view for this user.
+ return res.status(200).json({
+ userClips: clips,
+ created_by: userID,
+ success: true
+ });
+ });
+};
\ No newline at end of file
diff --git a/src/middlewares/middlewares.js b/src/middlewares/middlewares.js
index 3ecdf4a..7064fc1 100644
--- a/src/middlewares/middlewares.js
+++ b/src/middlewares/middlewares.js
@@ -3,5 +3,6 @@ import { validateCookie } from './validateCookie';
import { renderLandingPage } from './renderLandingPage';
import{ aboutPage } from './aboutPage';
import { validateOwnDomain, urlAlreadyTrimmedByUser, stripUrl, customUrlExists } from './validateUrl';
+import { getSyncedData } from './getSyncData';
-export { renderLandingPage, aboutPage, validateOwnDomain, validateCookie, urlAlreadyTrimmedByUser, stripUrl, customUrlExists };
+export { renderLandingPage, aboutPage, validateOwnDomain, validateCookie, urlAlreadyTrimmedByUser, stripUrl, customUrlExists, getSyncedData };
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 322f92d..93cd76b 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -7,7 +7,8 @@ import {
validateCookie,
urlAlreadyTrimmedByUser,
stripUrl,
- customUrlExists
+ customUrlExists,
+ getSyncedData
} from "../middlewares/middlewares";
import {
getUrlAndUpdateCount,
@@ -19,6 +20,8 @@ import { getUrlClickMetrics } from '../controllers/metricsController';
export const initRoutes = app => {
app.get("/", validateCookie, renderLandingPage);
+ app.post("/sync-device", validateCookie, getSyncedData);
+ app.delete('/', deleteUrl);
app.get("/about", (req, res) => res.status(200).render("about"));
app.get("/docs", (req,res)=>res.status(200).redirect("https://documenter.getpostman.com/view/4447136/SzYaWe6j?version=latest"));
app.post("/", [check('long_url').isString().not().isEmpty().withMessage('Long url cannot be empty'),
diff --git a/src/views/index.ejs b/src/views/index.ejs
index 7aba182..f082efd 100644
--- a/src/views/index.ejs
+++ b/src/views/index.ejs
@@ -118,9 +118,11 @@
+
+
- <%=clip.long_url%>
+ <%=clip.long_url%>
|
<%=clip.expiry_date ? new Date(clip.expiry_date).toDateString() : '—' %>
@@ -130,6 +132,22 @@
+
+
+
+ <% if (userClips.length === 0) {%>
+
+ <%} else {%>
+ Here is your syncID: <%= created_by %>
+ <%}%>
+
diff --git a/src/views/partials/footer.ejs b/src/views/partials/footer.ejs
index cc34c4b..0aec840 100644
--- a/src/views/partials/footer.ejs
+++ b/src/views/partials/footer.ejs
@@ -90,6 +90,20 @@
copyToClipboard(x);
})
}
+
+ function deleteUrl(clip) {
+ const el = clip.closest('tr');
+ if (window.confirm(`Do you really want to delete "${el.childNodes[7].innerText}"`)) {
+ el.remove()
+ }
+ fetch('/', {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({urlId: clip.dataset.id})
+ })
+ }
|