Added 'memories' tab

Signed-off-by: James Ketrenos <james_git@ketrenos.com>
This commit is contained in:
James Ketr 2018-09-12 01:52:02 -07:00
parent dc0dc0da3c
commit 517ea8f66e
3 changed files with 149 additions and 48 deletions

View File

@ -16,7 +16,7 @@
], ],
"dependencies": { "dependencies": {
"polymer": "Polymer/polymer#^1.4.0", "polymer": "Polymer/polymer#^1.4.0",
"paper-tabs": "PolymerElements/paper-tabs#^1.5.0", "paper-tabs": "PolymerElements/paper-tabs#^2.1.1",
"iron-pages": "PolymerElements/iron-pages#^1.0.7", "iron-pages": "PolymerElements/iron-pages#^1.0.7",
"paper-toast": "PolymerElements/paper-toast#^1.2.2", "paper-toast": "PolymerElements/paper-toast#^1.2.2",
"paper-material": "PolymerElements/paper-material#^1.0.6", "paper-material": "PolymerElements/paper-material#^1.0.6",
@ -60,6 +60,7 @@
"paper-checkbox": "^1.2.0", "paper-checkbox": "^1.2.0",
"webcomponentsjs": "^v1.1.0", "webcomponentsjs": "^v1.1.0",
"paper-icon-button": "^1.1.2", "paper-icon-button": "^1.1.2",
"iron-icons": "^1.1.3" "iron-icons": "^1.1.3",
"paper-tabs": "^1.5.0"
} }
} }

View File

@ -14,6 +14,7 @@
<link rel="import" href="../../bower_components/iron-icons/iron-icons.html"> <link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html"> <link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html"> <link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html">
<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html"> <link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-checkbox/paper-checkbox.html"> <link rel="import" href="../../bower_components/paper-checkbox/paper-checkbox.html">
@ -24,6 +25,8 @@
<link rel="import" href="../../bower_components/paper-radio-group/paper-radio-group.html"> <link rel="import" href="../../bower_components/paper-radio-group/paper-radio-group.html">
<link rel="import" href="../../bower_components/paper-radio-button/paper-radio-button.html"> <link rel="import" href="../../bower_components/paper-radio-button/paper-radio-button.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html"> <link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../../bower_components/paper-tabs/paper-tab.html">
<link rel="import" href="../../bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="../../bower_components/paper-toast/paper-toast.html"> <link rel="import" href="../../bower_components/paper-toast/paper-toast.html">
<script src="../../bower_components/moment/moment.js"></script> <script src="../../bower_components/moment/moment.js"></script>
@ -56,7 +59,10 @@
padding: 0.5em 1em; padding: 0.5em 1em;
background: #ddd; background: #ddd;
box-shadow: 0px 0px 5px black; box-shadow: 0px 0px 5px black;
box-sizing: border-box;
height: 64px;
} }
#breadcrumb { #breadcrumb {
padding: 0.5em; padding: 0.5em;
} }
@ -70,23 +76,32 @@
overflow-y: scroll; overflow-y: scroll;
} }
#albums > div { #pages > div {
box-sizing: border-box;
position: relative;
height: 100%;
}
#pages > div > div {
margin: 0.5em; margin: 0.5em;
cursor: pointer; cursor: pointer;
} }
#searchBox { #drawer {
} }
#searchContents { #pages {
position: relative;
height: calc(100vh - 64px);
box-sizing: border-box; box-sizing: border-box;
height: 100%;
} }
#searchMode { #tabs {
padding: 0.5em 1em;
background: #ddd; background: #ddd;
color: #444; color: #444;
box-sizing: border-box;
height: 64px;
box-shadow: 0px 0px 5px black;
} }
#breadcrumb > div { #breadcrumb > div {
@ -201,18 +216,21 @@
border: 3px solid blue; border: 3px solid blue;
}; };
} }
</style> </style>
<app-location route="{{route}}"></app-location> <app-location route="{{route}}"></app-location>
<app-drawer-layout id="albumLayout" force-narrow=true> <app-drawer-layout id="albumLayout"><!--force-narrow=true-->
<app-drawer id="searchBox" slot="drawer"> <app-drawer id="drawer" slot="drawer">
<div id="searchContents" class="layout vertical"> <paper-tabs attr-for-selected="tab" id="tabs" selected="{{mode}}">
<div id="searchMode" class="layout horizontal justified"> <paper-tab tab="time"><paper-icon-button icon="date-range"></paper-icon-button></paper-tab>
<paper-icon-button icon="folder"></paper-icon-button> <paper-tab tab="memories"><paper-icon-button icon="today"></paper-icon-button></paper-tab>
<paper-icon-button icon="date-range"></paper-icon-button> <paper-tab tab="albums"><paper-icon-button icon="folder"></paper-icon-button></paper-tab>
<paper-icon-button icon="today"></paper-icon-button> </paper-tabs>
<iron-pages id="pages" attr-for-selected="id" selected="[[mode]]">
<div id="time"><div>... time slider ...</div></div>
<div id="memories" class="flex layout vertical">
<div>... calendar ...</div>
</div> </div>
<div id="albums" class="flex layout vertical"> <div id="albums" class="flex layout vertical">
<template is="dom-repeat" items="[[breadcrumb(path)]]"> <template is="dom-repeat" items="[[breadcrumb(path)]]">
@ -222,12 +240,12 @@
<div on-tap="loadPath">[[item.name]]</div> <div on-tap="loadPath">[[item.name]]</div>
</template> </template>
</div> </div>
</div> </iron-pages>
</app-drawer> </app-drawer>
<app-header-layout> <app-header-layout>
<app-header reveals slot="header"> <app-header reveals slot="header">
<div id="header" class="layout horizontal center"> <div id="header" class="layout horizontal center">
<paper-icon-button icon="search" on-tap="searchBoxToggle"></paper-icon-button> <paper-icon-button icon="search" on-tap="drawerToggle"></paper-icon-button>
<div id="breadcrumb" class="horizontal layout center"> <div id="breadcrumb" class="horizontal layout center">
<template is="dom-repeat" items="[[breadcrumb(path)]]"> <template is="dom-repeat" items="[[breadcrumb(path)]]">
<div on-tap="loadPath">[[item.name]] /</div> <div on-tap="loadPath">[[item.name]] /</div>
@ -266,7 +284,7 @@
type: String, type: String,
value: "by-date" value: "by-date"
}, },
"loading": Boolean, loading: Boolean,
pendingPhotos: { pendingPhotos: {
type: Array, type: Array,
value: [] value: []
@ -295,13 +313,42 @@
thumbnails: { thumbnails: {
type: Array, type: Array,
value: [] value: []
},
mode: {
type: String,
value: "time"
} }
}, },
observers: [
"widthChanged(calcWidth)",
"modeChanged(mode)"
],
shouldShowAlbums: function(order) { shouldShowAlbums: function(order) {
return order == "by-album"; return order == "by-album";
}, },
changeMode: function(event) {
var mode = event.currentTarget.icon;
if (this.mode != mode) {
this.mode = mode;
}
},
modeChanged: function(mode) {
console.log("Mode changed to " + mode);
this.path = "";
this.pendingPhotos = [];
this.visibleThumbs = [];
this.thumbnails = [];
this.cursor = null;
Polymer.dom(this.$.thumbnails).innerHTML = "";
this.next = false;
this.limit = undefined;
this._loadPhotos();
},
breadcrumb: function(path) { breadcrumb: function(path) {
var crumbs = path.split("/"), parts = []; var crumbs = path.split("/"), parts = [];
path = ""; path = "";
@ -317,20 +364,16 @@
return parts; return parts;
}, },
observers: [ drawerToggle: function(event) {
"widthChanged(calcWidth)" if (this.$.drawer.opened) {
], this.$.drawer.close();
searchBoxToggle: function(event) {
if (this.$.searchBox.opened) {
this.$.searchBox.close();
this.$.albumLayout.forceNarrow = true; this.$.albumLayout.forceNarrow = true;
this.$.searchBox.resetLayout(); this.$.drawer.resetLayout();
} else { } else {
this.$.searchBox.open(); this.$.drawer.open();
this.$.albumLayout.forceNarrow = false; this.$.albumLayout.forceNarrow = false;
if (window.innerWidth >= 800) { if (window.innerWidth >= 800) {
this.$.searchBox.persistent = true; this.$.drawer.persistent = true;
} }
} }
}, },
@ -754,13 +797,16 @@
query += key + "=" + encodeURIComponent(params[key]); query += key + "=" + encodeURIComponent(params[key]);
} }
var path = this.path || ""; var path = this.path || "", mode = this.mode;
if (mode != "albums") {
path = "/" + mode;
}
console.log("Requesting " + this.limit + " photos."); console.log("Requesting " + this.limit + " photos.");
window.fetch("api/v1/photos" + path + query, function(path, error, xhr) { window.fetch("api/v1/photos" + path + query, function(path, error, xhr) {
this.loading = false; this.loading = false;
if (path != (this.path || "")) { if ((mode != this.mode) || ((mode == "albums") && (path != (this.path || "")))) {
console.log("Skipping results for old query. Triggering re-fetch of photos for new path."); console.log("Skipping results for old query. Triggering re-fetch of photos for new path or mode.");
this._loadPhotos(); this._loadPhotos();
return; return;
} }
@ -865,22 +911,6 @@
} }
}.bind(this), 100); }.bind(this), 100);
window.setInterval(function() {
function isElementInViewport(el) {
var rect = el.getBoundingClientRect(),
vWidth = window.innerWidth || doc.documentElement.clientWidth,
vHeight = window.innerHeight || doc.documentElement.clientHeight;
// Return false if it's not in the viewport
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight) {
return false;
}
return true;
}
}.bind(this), 500);
this._loadAlbums(); this._loadAlbums();
this._loadPhotos(); this._loadPhotos();

View File

@ -29,6 +29,76 @@ const router = express.Router();
*/ */
router.get("/memories", function(req, res/*, next*/) {
let limit = parseInt(req.query.limit) || 50,
id, cursor, index;
if (req.query.next) {
let parts = req.query.next.split("_");
cursor = parts[0];
id = parseInt(parts[1]);
} else {
cursor = "";
id = -1;
}
if (id == -1) {
index = "";
} else {
if (id != -1) {
index = " AND ((taken=DATE(:cursor) AND id<"+id+ ") OR taken<DATE(:cursor))";
} else {
index = " AND (taken<=DATE(:cursor))";
}
}
let query = "SELECT * FROM photos WHERE strftime('%m%d',taken)=strftime('%m%d',CURRENT_TIMESTAMP) " + index + " ORDER BY taken DESC,id DESC LIMIT " + (limit * 2 + 1);
console.log("Memories query", query);
return photoDB.sequelize.query(query, {
replacements: {
cursor: cursor
},
type: photoDB.Sequelize.QueryTypes.SELECT
}).then(function(photos) {
photos.forEach(function(photo) {
for (var key in photo) {
if (photo[key] instanceof Date) {
photo[key] = moment(photo[key]);
}
}
});
if (cursor) {
cursor = moment(cursor);
photos = photos.filter(function(photo) {
if (!cursor.isSame(photo.taken, "day")) {
return true;
}
return photo.id < id;
});
}
let more = photos.length > limit; /* We queried one extra item to see if there are more than LIMIT available */
if (more) {
photos.splice(limit);
}
let results = {
items: photos
};
if (more) {
results.more = true;
}
return res.status(200).json(results);
}).catch(function(error) {
console.error("Query failed: " + query);
return Promise.reject(error);
});
});
router.get("/*", function(req, res/*, next*/) { router.get("/*", function(req, res/*, next*/) {
let limit = parseInt(req.query.limit) || 50, let limit = parseInt(req.query.limit) || 50,
id, cursor, index; id, cursor, index;