Commit 44a71dd5 authored by MrMan's avatar MrMan

Write enough SystemJS integration to get inbox to show

parent 1d7af6ca
/* global m */
var LHS_NAV_ITEMS = [
{
title: "Inbox",
iconImgSrc: "static/images/icons/inbox-white.svg",
badgeCSSBgColor: "#2285c6",
badgeContent: 2,
route: "/inbox",
},
{
title: "Starred",
iconImgSrc: "static/images/icons/star-full-white.svg",
route: "/starred",
},
{
title: "Draft",
iconImgSrc: "static/images/icons/file-line-white.svg",
route: "/draft",
},
{
title: "Important",
iconImgSrc: "static/images/icons/exclamation-triangle-white.svg",
route: "/important",
},
];
var LHS_LABEL_ITEMS = [
{
title: "Work",
badgeCSSBgColor: "#02bfc0",
badgeEmptySquare: true,
},
{
title: "Private",
badgeCSSBgColor: "#f9d47b",
badgeEmptySquare: true,
},
{
title: "Priority",
badgeCSSBgColor: "#9391e5",
badgeEmptySquare: true,
},
];
var ONE_MINUTE_MS = 1000 * 60;
var ONE_HOUR_MS = 60 * ONE_MINUTE_MS;
var ONE_DAY_MS = 24 * ONE_HOUR_MS;
var ONE_WEEK_MS = 27 * ONE_DAY_MS;
function randomPastDate() {
var mins = Math.floor(Math.random() * 60) * ONE_MINUTE_MS;
var hours = Math.floor(Math.random() * 24) * ONE_HOUR_MS;
var days = Math.floor(Math.random() * 7) * ONE_DAY_MS;
return new Date(new Date().getTime() - mins - hours - days);
}
var EMAILS = [
{
id: "QyLruCuUQ4SOy0JTkOuJ3VdOk8HbDuw3",
from: {name: "Victor Erixon", email: "victor.erixon@example.com"},
subject: "Theories of Design",
text: "Molestias deserunt veritatis modi ut fugiat sint. Dolorum vel autem sint. Dolores accusamus dolores occaecati tenetur adipisci asperiores",
sendDate: randomPastDate(),
},
{
id: "KpjgJQT8nIBdLj1NCjCPPvZJBtzhhixn",
from: {name: "Jacob Hubertus", email: "jacob.hubertus@example.com"},
subject: "Street Photography",
text: "Molestias deserunt veritatis modi ut fugiat sint. Dolorum vel autem sint. Dolores accusamus dolores occaecati tenetur adipisci asperiores",
sendDate: randomPastDate(),
},
{
id: "JiTTyuQ2XUakNd6xSeolyoWEpeNjOwJi",
from: {name: "August Berglund", email: "august.berglund@example.com"},
subject: "HTML+CSS Tutorials?",
text: "Molestias deserunt veritatis modi ut fugiat sint. Dolorum vel autem sint. Dolores accusamus dolores occaecati tenetur adipisci asperiores",
sendDate: randomPastDate(),
},
{
id: "XlwnoP5EUNiauVn8anBba1Ui2NeSwUCg",
from: {name: "Rick Brunstedt", email: "rick.brunstedt@example.com"},
subject: "Train Crossing",
text: "Molestias deserunt veritatis modi ut fugiat sint. Dolorum vel autem sint. Dolores accusamus dolores occaecati tenetur adipisci asperiores",
sendDate: randomPastDate(),
},
{
id: "w5smg1YFSo9ANXZQe56bQkNxcX1mJK16",
from: {name: "Webydo Team", email: "team@webydo.com"},
subject: "Does Webydo have what you're looking for? ",
text: "Molestias deserunt veritatis modi ut fugiat sint. Dolorum vel autem sint. Dolores accusamus dolores occaecati tenetur adipisci asperiores",
sendDate: randomPastDate(),
},
];
EMAILS = EMAILS.sort(function(a, b) {
var aTime = a.sendDate.getTime();
var bTime = b.sendDate.getTime();
if (aTime < aTime) { return -1; }
if (bTime > bTime) { return 1; }
return 0;
});
var ELLIPSIS_CHAR_LIMIT = 60;
/**
* Email service that will manage checking
*
* Note: Mithril can't redraw subtrees yet (https://github.com/MithrilJS/mithril.js/issues/1907), so m.redraw() is everywhere
*/
function EmailService() {
this.selectedEmailId = null;
this.checkedEmailIds = {};
this._allEmailsChecked = false;
}
EmailService.prototype.selectEmailById = function(id, opts) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
console.log("[email-svc] selected email with id:", id);
this.selectedEmailId = id;
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.resetSelectedEmailId = function(opts) {
this.selectedEmailId = null;
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.toggleSelectionStatusById = function(id, opts) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
// Reset if currently selected and exit early
if (this.getEmailSelectedStatusById(id)) {
this.resetSelectedEmailId();
return;
}
this.selectEmailById(id);
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.getEmailSelectedStatusById = function(id) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
return this.selectedEmailId === id;
};
EmailService.prototype.checkEmailById = function(id, opts) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
console.log("[email-svc] checked email with id:", id);
this.checkedEmailIds[id] = true;
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.resetEmailCheckedStatusById = function(id, opts) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
delete this.checkedEmailIds[id]; // This is kinda bad to do a lot
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.toggleCheckedStatusById = function(id, opts) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
// Reset if currently selected and exit early
if (this.getEmailCheckedStatusById(id)) {
this.resetEmailCheckedStatusById(id);
return;
}
this.checkEmailById(id);
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.getEmailCheckedStatusById = function(id) {
if (!id) { throw new Error("Invalid email object, ID is missing"); }
return this._allEmailsChecked ? true : id in this.checkedEmailIds;
};
EmailService.prototype.clearCheckedEmails = function(opts) {
this._allEmailsChecked = false;
this.checkedEmailIds = {};
console.log("[email-svc] cleared checked emails");
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.checkAllEmails = function(opts) {
this._allEmailsChecked = true;
console.log("[email-svc] checked all emails");
if (opts && opts.redraw) { m.redraw(); }
};
EmailService.prototype.allEmailsChecked = function(opts) {
return this._allEmailsChecked;
};
EmailService.prototype.oneOrMoreEmailsChecked = function(opts) {
// Object.keys isn't supported on some IE browsers, but Mithril doesn't support older than IE11 anyway IIRC
return this._allEmailsChecked || Object.keys(this.checkedEmailIds).length > 0;
};
EmailService.prototype.getEmails = function() {
var randomWaitMs = 1000 + Math.floor(Math.random() * 3) * 1000;
return new Promise(function(resolve, reject) {
// After some random wait, return the global EMAILS we've always used
setTimeout(function() {
resolve(EMAILS);
}, randomWaitMs);
});
};
var EMAIL_SVC = new EmailService();
m.route(document.body, "/inbox", {
"/inbox": {
render: function() {
return m(MainLayout, m(WithRouteTriggeredLoader, {
title: "Loading inbox...",
// This function provides the data
loadFn: function() { return EMAIL_SVC.getEmails(); },
// This function provides the component to WithRouteTriggeredLoader
childFn: function(data) { return m(InboxMail, {emails: data}); }
}));
}
},
"/starred": {
render: function() {
return m(MainLayout, m(WithRouteTriggeredLoader, {
title: "Loading starred emails...",
// This function provides the data
loadFn: function() { return EMAIL_SVC.getEmails(); },
// This function provides the component to WithRouteTriggeredLoader
childFn: function(data) { return m(StarredMail, {emails: data}); }
}));
}
},
"/draft": {
render: function() {
return m(MainLayout, m(WithRouteTriggeredLoader, {
title: "Loading draft emails...",
// This function provides the data
loadFn: function() { return EMAIL_SVC.getEmails(); },
// This function provides the component to WithRouteTriggeredLoader
childFn: function(data) { return m(DraftMail, {emails: data}); }
}));
}
},
"/important": {
render: function() {
return m(MainLayout, m(WithRouteTriggeredLoader, {
title: "Loading important emails...",
// This function provides the data
loadFn: function() { return EMAIL_SVC.getEmails(); },
// This function provides the component to WithRouteTriggeredLoader
childFn: function(data) { return m(ImportantMail, {emails: data}); }
}));
}
},
/* global document, m, System */
System.register([
"./services/email.js",
"./components/main-layout.js",
"./components/with-route-triggered-loader.js",
"./components/inbox-mail.js",
], function(exportModule, moduleCtx) {
return {
execute: function() {
Promise.all([
moduleCtx.import("./services/email.js"),
moduleCtx.import("./components/main-layout.js"),
moduleCtx.import("./components/with-route-triggered-loader.js"),
moduleCtx.import("./components/inbox-mail.js"),
])
.then(function(deps) {
var EmailSvc = deps.shift().service;
var MainLayout = deps.shift().component;
var WithRouteTriggeredLoader = deps.shift().component;
var InboxMail = deps.shift().component;
// var StarredMail = deps.shift();
// var DraftMail = deps.shift();
// var ImportantMail = deps.shift();
// Set up the app
m.route(document.body, "/inbox", {
"/inbox": {
render: function() {
return m(MainLayout, m(WithRouteTriggeredLoader, {
title: "Loading inbox...",
// This function provides the data
loadFn: function() { return EmailSvc.getEmails(); },
// This function provides the component to WithRouteTriggeredLoader
childFn: function(data) { return m(InboxMail, {emails: data}); }
}));
}
},
// "/starred": {
// render: function() {
// return m(MainLayout, m(WithRouteTriggeredLoader, {
// title: "Loading starred emails...",
// // This function provides the data
// loadFn: function() { return EmailSvc.getEmails(); },
// // This function provides the component to WithRouteTriggeredLoader
// childFn: function(data) { return m(StarredMail, {emails: data}); }
// }));
// }
// },
// "/draft": {
// render: function() {
// return m(MainLayout, m(WithRouteTriggeredLoader, {
// title: "Loading draft emails...",
// // This function provides the data
// loadFn: function() { return EmailSvc.getEmails(); },
// // This function provides the component to WithRouteTriggeredLoader
// childFn: function(data) { return m(DraftMail, {emails: data}); }
// }));
// }
// },
// "/important": {
// render: function() {
// return m(MainLayout, m(WithRouteTriggeredLoader, {
// title: "Loading important emails...",
// // This function provides the data
// loadFn: function() { return EmailSvc.getEmails(); },
// // This function provides the component to WithRouteTriggeredLoader
// childFn: function(data) { return m(ImportantMail, {emails: data}); }
// }));
// }
// },
});
// This module exports nothing, just runs once
exportModule({});
});
}, // /execute
};
});
var EmailListItem = {
view: function(vnode) {
var email = vnode.attrs.email;
var textContent = email.text || "";
var formattedSendDate = email.sendDate.toLocaleString();
var isSelected = EMAIL_SVC.getEmailSelectedStatusById(email.id);
var isChecked = EMAIL_SVC.getEmailCheckedStatusById(email.id);
var additionalClasses = [];
if (isSelected) { additionalClasses.push("selected"); }
return m(
".component.email-list-item.clickable",
{
class: additionalClasses.join(" "),
onclick: function() { EMAIL_SVC.toggleSelectionStatusById(email.id); }
},
[
m(".header", [
m(".lhs", [
m("input[type=checkbox]", {
checked: isChecked,
onclick: function() { EMAIL_SVC.toggleCheckedStatusById(email.id); }
}),
m("span.sm-margin-left.sender", email.from.name),
m("span.sm-margin-left.tag", email.label),
]),
m(".rhs", [
m(".date", formattedSendDate)
])
]),
m(".blurb.md-margin-top", [
m("p.subject", email.subject),
// This is not a great way to do ellipsis but... it'll work for the demo.
m("p.body-preview", textContent.substring(0, ELLIPSIS_CHAR_LIMIT) + "..."),
]),
]);
}
};
var EmailList = {
oninit: function() {
this.selectedEmails = {};
},
view: function(vnode) {
var emails = vnode.attrs.emails || [];
return m(".component.email-list", emails.map(function(email) {
return m(EmailListItem, {
email: email,
onEmailSelect: function(email) { this.selectedEmails[email.id] = true; },
/* global System */
System.register([
"../services/email.js",
], function(exportModule, moduleCtx) {
return {
execute: function() {
Promise.all([
moduleCtx.import("../services/email.js",)
]).then(function(deps) {
var EmailSvc = deps.shift().service;
var ELLIPSIS_CHAR_LIMIT = 60;
var EmailListItem = {
view: function(vnode) {
var email = vnode.attrs.email;
var textContent = email.text || "";
var formattedSendDate = email.sendDate.toLocaleString();
var isSelected = EmailSvc.getEmailSelectedStatusById(email.id);
var isChecked = EmailSvc.getEmailCheckedStatusById(email.id);
var additionalClasses = [];
if (isSelected) { additionalClasses.push("selected"); }
return m(
".component.email-list-item.clickable",
{
class: additionalClasses.join(" "),
onclick: function() { EmailSvc.toggleSelectionStatusById(email.id); }
},
[
m(".header", [
m(".lhs", [
m("input[type=checkbox]", {
checked: isChecked,
onclick: function() { EmailSvc.toggleCheckedStatusById(email.id); }
}),
m("span.sm-margin-left.sender", email.from.name),
m("span.sm-margin-left.tag", email.label),
]),
m(".rhs", [
m(".date", formattedSendDate)
])
]),
m(".blurb.md-margin-top", [
m("p.subject", email.subject),
// This is not a great way to do ellipsis but... it'll work for the demo.
m("p.body-preview", textContent.substring(0, ELLIPSIS_CHAR_LIMIT) + "..."),
]),
]);
}
};
var EmailList = {
oninit: function() {
this.selectedEmails = {};
},
view: function(vnode) {
var emails = vnode.attrs.emails || [];
return m(".component.email-list", emails.map(function(email) {
return m(EmailListItem, {
email: email,
onEmailSelect: function(email) { this.selectedEmails[email.id] = true; },
});
}));
}
};
exportModule({component: EmailList});
});
}));
}
};
},
};
});
var InboxMail = {
view: function(vnode) {
var newCount = vnode.attrs.newCount;
var emails = vnode.attrs.emails;
/* global System */
System.register([
"./rhs-header.js",
"./mail-page-subnav.js",
"./email-list.js",
], function(exportModule, moduleCtx) {
return {
execute: function() {
return m(".component.inbox-rhs", [
// Colored header
m(RHSHeader, {class: "inbox-blue-bg white-fg", title: "Inbox", newCount: newCount}),
Promise.all([
moduleCtx.import("./rhs-header.js"),
moduleCtx.import("./mail-page-subnav.js"),
moduleCtx.import("./email-list.js"),
])
.then(function(deps) {
var RHSHeader = deps.shift().component;
var MailPageSubNav = deps.shift().component;
var EmailList = deps.shift().component;
// Container with buttons
m(MailPageSubNav),
var InboxMail = {
view: function(vnode) {
var newCount = vnode.attrs.newCount;
var emails = vnode.attrs.emails;
// EmailList
m(EmailList, {emails: vnode.attrs.emails}),
]);
}
};
return m(".component.inbox-rhs", [
// Colored header
m(RHSHeader, {class: "inbox-blue-bg white-fg", title: "Inbox", newCount: newCount}),
// Container with buttons
m(MailPageSubNav),
// EmailList
m(EmailList, {emails: vnode.attrs.emails}),
]);
}
};
exportModule({component: InboxMail});
});
},
};
});
var HeaderButton = {
view: function(vnode) {
var text = vnode.attrs.title || "Compose Mail";
var iconName = vnode.attrs.iconName || "pencil-white.svg";
var iconPrefix = "static/images/icons/";
return m("button", {class: "component header-button"}, [
m("span", text),
m("img", {class: "sm-margin-left icon", src: iconPrefix + iconName }),
]);
}
};
var LHSNavHeader = {
view: function() {
return m("header", [
m(HeaderButton),
]);
}
};
var Badge = {
view: function(vnode) {
var styles = [];
var content = vnode.attrs.content || "";
var bgColor = vnode.attrs.cssBgColor || "gray";
var color = vnode.attrs.cssBgColor || "white";
var emptySquare = vnode.attrs.emptySquare;
var classes = ["component", "badge"];
styles.push("background-color:" + vnode.attrs.cssBgColor);
styles.push("color:" + vnode.attrs.cssColor);
var combinedStyles = styles.join(";");
if (emptySquare) { classes.push("empty-square"); }
return m("div", {class: classes.join(" "), style: combinedStyles}, content);
}
};
var NavItem = {
view: function(vnode) {
var data = vnode.attrs.data || {};
var title = data.title || "Placeholder";
var iconImgSrc = data.iconImgSrc;
var badgeContent = data.badgeContent || 0;
var badgeCSSBgColor = data.badgeCSSBgColor;
var badgeEmptySquare = data.badgeEmptySquare;
var children = [title];
var classes = ["component", "nav-item", "md-padding-horiz"];
if (data.selected) { classes.push("selected"); }
// If there's an icon specified display it
if (iconImgSrc) {
children.unshift(m("img", {class: "sm-margin-left sm-margin-right icon", src: iconImgSrc}));
} else {
children.unshift(m("div", {class: "placeholder-1em"}));
}
// If badgeColor and badgeContent are specified then show a badge
if (badgeEmptySquare && badgeCSSBgColor || badgeContent && badgeCSSBgColor) {
children.push(m("div", {class: "badge-container float-right"}, [
m(Badge, {
class: "float-right",
cssBgColor: badgeCSSBgColor,
content: badgeContent,
emptySquare: badgeEmptySquare
})
]));
}
return m("li.clickable", {
class: classes.join(" "),
onclick: function(e) {
// Navigate to the appropriate link for this
m.route.set(data.route);
},
}, children);
}
};
var NavItemListing = {
view: function(vnode) {
var navItems = vnode.attrs.items || [];
var children = navItems.map(function(i) {
return m(NavItem, {data: i});
});
return m("ul", {class: "component nav-item-listing"}, children);
}
};
var LHSNavPageList = {
view: function() {
// Mark the nav items with the correct selected property
var currentPath = m.route.get();
var navItems = LHS_NAV_ITEMS.map(function(item) {
item.selected = currentPath === item.route;
return item;
});
return m(NavItemListing, {items: navItems});
}
};
var LHSNavLabelList = {
view: function() {
var labelItems = LHS_LABEL_ITEMS;
return m(NavItemListing, {items: labelItems});
}
};
var LHSNav = {
view: function() {
return m("div", {class: "component lhs-nav"}, [
m(LHSNavHeader),
m(LHSNavPageList),
m("hr"),
m("h2", {class: "slim-text sm-padding-horiz"}, "Labels"),
m(LHSNavLabelList),
m("hr"),
]);
}
};
/* global m, System */
System.register([], function(exportModule, moduleCtx) {
return {
execute: function() {
var LHS_NAV_ITEMS = [
{
title: "Inbox",
iconImgSrc: "static/images/icons/inbox-white.svg",
badgeCSSBgColor: "#2285c6",
badgeContent: 2,
route: "/inbox",
},
{
title: "Starred",
iconImgSrc: "static/images/icons/star-full-white.svg",
route: "/starred",
},
{
title: "Draft",
iconImgSrc: "static/images/icons/file-line-white.svg",
route: "/draft",
},
{
title: "Important",
iconImgSrc: "static/images/icons/exclamation-triangle-white.svg",
route: "/important",
},
];
var LHS_LABEL_ITEMS = [
{
title: "Work",
badgeCSSBgColor: "#02bfc0",
badgeEmptySquare: true,
},
{
title: "Private",
badgeCSSBgColor: "#f9d47b",
badgeEmptySquare: true,
},
{