diff --git a/client/css/home.styl b/client/css/home.styl
index 18fc877..0c6d549 100644
--- a/client/css/home.styl
+++ b/client/css/home.styl
@@ -3,6 +3,27 @@
h1
margin-top: 0
+ margin-bottom: 0.5em
.message
margin-bottom: 2em
+
+ .post-container
+ display: flex
+ align-items: center
+ justify-content: center
+
+ nav
+ margin-bottom: 0.5em
+ a
+ padding: 0.5em
+
+ form
+ width: auto
+
+ aside
+ margin-top: 0.2em
+ margin-bottom: 1em
+
+ footer
+ font-size: 80%
diff --git a/client/css/main.styl b/client/css/main.styl
index 0f114f9..8fd92f9 100644
--- a/client/css/main.styl
+++ b/client/css/main.styl
@@ -132,7 +132,6 @@ a .access-key
.access-key
text-decoration: underline
.thumbnail
- vertical-align: middle
width: 1.5em
height: 1.5em
margin: calc((2.3em - 1.5em) / 2)
@@ -161,15 +160,19 @@ a .access-key
.thumbnail
/*background-image: attr(data-src url)*/ /* not available yet */
+ vertical-align: middle
background-repeat: no-repeat
background-size: cover
background-position: center
display: inline-block
-
-.thumbnail img
- opacity: 0
- width: 100%
- height: 100%
+ img
+ opacity: 0
+ width: 100%
+ height: 100%
+span>.thumbnail
+ width: 20px
+ height: 20px
+ margin: 0 0.4em 0 0.4em
.flexbox-dummy
height: 0 !important
diff --git a/client/css/posts.styl b/client/css/posts.styl
new file mode 100644
index 0000000..3ea0f8b
--- /dev/null
+++ b/client/css/posts.styl
@@ -0,0 +1,29 @@
+@import colors
+
+.post-container
+ text-align: center
+ .post-content
+ text-align: left
+ margin: 0 auto
+ max-height: 100%
+ max-width: 100%
+ object-fit: contain
+ position: relative
+
+ img, object, video, .post-overlay
+ position: absolute
+ height: 100%
+ width: 100%
+ left: 0
+ right: 0
+ top: 0
+ bottom: 0
+
+ .post-overlay>*
+ position: absolute
+ left: 0
+ right: 0
+ top: 0
+ bottom: 0
+ width: 100%
+ height: 100%
diff --git a/client/html/home.tpl b/client/html/home.tpl
index b14b0c9..78d995f 100644
--- a/client/html/home.tpl
+++ b/client/html/home.tpl
@@ -2,7 +2,53 @@
+ <% if (['image', 'animation'].includes(ctx.post.type)) { %>
+
+
+
+ <% } else if (ctx.post.type === 'flash') { %>
+
+
+
+ <% } else if (ctx.post.type === 'video') { %>
+
+ <% if (ctx.post.flags.includes('loop')) { %>
+
diff --git a/client/js/controllers/home_controller.js b/client/js/controllers/home_controller.js
index 25800d0..cd44a61 100644
--- a/client/js/controllers/home_controller.js
+++ b/client/js/controllers/home_controller.js
@@ -24,13 +24,22 @@ class HomeController {
api.get('/info')
.then(response => {
this._homeView.render({
+ canListPosts: api.hasPrivilege('posts:list'),
+ canListComments: api.hasPrivilege('comments:list'),
+ canListTags: api.hasPrivilege('tags:list'),
+ canListUsers: api.hasPrivilege('users:list'),
diskUsage: response.diskUsage,
postCount: response.postCount,
featuredPost: response.featuredPost,
});
},
response => {
- this._homeView.render({});
+ this._homeView.render({
+ canListPosts: api.hasPrivilege('posts:list'),
+ canListComments: api.hasPrivilege('comments:list'),
+ canListTags: api.hasPrivilege('tags:list'),
+ canListUsers: api.hasPrivilege('users:list'),
+ });
events.notify(events.Error, response.description);
});
}
diff --git a/client/js/controls/post_content_control.js b/client/js/controls/post_content_control.js
new file mode 100644
index 0000000..9775ec4
--- /dev/null
+++ b/client/js/controls/post_content_control.js
@@ -0,0 +1,79 @@
+'use strict';
+
+const views = require('../util/views.js');
+const optimizedResize = require('../util/optimized_resize.js');
+
+class PostContentControl {
+ constructor(containerNode, post, viewportSizeCalculator) {
+ post.canvasWidth = post.canvasWidth || 800;
+ post.canvasHeight = post.canvasHeight || 450;
+
+ this._post = post;
+ this._viewportSizeCalculator = viewportSizeCalculator;
+ this._containerNode = containerNode;
+ this._template = views.getTemplate('post-content');
+
+ this._install();
+
+ this._currentFitFunction = this.fitBoth;
+ this._currentFitFunction();
+ }
+
+ fitWidth() {
+ this._currentFitFunction = this.fitWidth;
+ const mul = this._post.canvasHeight / this._post.canvasWidth;
+ this._resize(this._viewportWidth, this._viewportWidth * mul);
+ }
+
+ fitHeight() {
+ this._currentFitFunction = this.fitHeight;
+ const mul = this._post.canvasWidth / this._post.canvasHeight;
+ this._resize(this._viewportHeight * mul, this._viewportHeight);
+ }
+
+ fitBoth() {
+ this._currentFitFunction = this.fitBoth;
+ let mul = this._post.canvasHeight / this._post.canvasWidth;
+ if (this._viewportWidth * mul < this._viewportHeight) {
+ this._resize(this._viewportWidth, this._viewportWidth * mul);
+ } else {
+ mul = this._post.canvasWidth / this._post.canvasHeight;
+ this._resize(this._viewportHeight * mul, this._viewportHeight);
+ }
+ }
+
+ get _viewportWidth() {
+ return this._viewportSizeCalculator()[0];
+ }
+
+ get _viewportHeight() {
+ return this._viewportSizeCalculator()[1];
+ }
+
+ _resize(width, height) {
+ const postContentNode =
+ this._containerNode.querySelector('.post-content');
+ postContentNode.style.width = width + 'px';
+ postContentNode.style.height = height + 'px';
+ }
+
+ _refreshSize() {
+ this._currentFitFunction();
+ }
+
+ _install() {
+ const postContentNode = this._template({
+ post: this._post,
+ });
+ this._containerNode.appendChild(postContentNode);
+ optimizedResize.add(() => this._refreshSize());
+ views.monitorNodeRemoval(
+ this._containerNode, () => { this._uninstall(); });
+ }
+
+ _uninstall() {
+ optimizedResize.remove(() => this._refreshSize());
+ }
+}
+
+module.exports = PostContentControl;
diff --git a/client/js/util/optimized_resize.js b/client/js/util/optimized_resize.js
new file mode 100644
index 0000000..b8e1342
--- /dev/null
+++ b/client/js/util/optimized_resize.js
@@ -0,0 +1,33 @@
+'use strict';
+
+let callbacks = [];
+let running = false;
+
+function resize() {
+ if (!running) {
+ running = true;
+ if (window.requestAnimationFrame) {
+ window.requestAnimationFrame(runCallbacks);
+ } else {
+ setTimeout(runCallbacks, 66);
+ }
+ }
+}
+
+function runCallbacks() {
+ callbacks.forEach(function(callback) {
+ callback();
+ });
+ running = false;
+}
+
+function add(callback) {
+ callbacks.push(callback);
+}
+
+function remove(callback) {
+ callbacks = callbacks.filter(c => c !== callback);
+}
+
+window.addEventListener('resize', resize);
+module.exports = {add: add, remove: remove};
diff --git a/client/js/util/views.js b/client/js/util/views.js
index 656ab04..10e6b5c 100644
--- a/client/js/util/views.js
+++ b/client/js/util/views.js
@@ -141,19 +141,27 @@ function makeColorInput(options) {
'label', {class: 'color'}, colorInput + textInput);
}
+function makePostLink(id) {
+ return makeNonVoidElement('a', {
+ 'href': '/post/' + id,
+ }, '@' + id);
+}
+
function makeTagLink(name) {
- let category = null;
- try {
- category = tags.getTagByName(name).category;
- } catch (e) {
- category = 'unknown';
- }
+ const tag = tags.getTagByName(name);
+ let category = tag ? tag.category : 'unknown';
return makeNonVoidElement('a', {
'href': '/tag/' + name,
'class': 'tag-' + category,
}, name);
}
+function makeUserLink(user) {
+ return makeNonVoidElement('span', {class: 'user'},
+ makeThumbnail(user.avatarUrl) +
+ makeNonVoidElement('a', {'href': '/user/' + user.name}, user.name));
+}
+
function makeFlexboxAlign(options) {
return Array.from(misc.range(20))
.map(() => '