diff --git a/client/css/posts.styl b/client/css/posts.styl
index 38123ca..c3ce945 100644
--- a/client/css/posts.styl
+++ b/client/css/posts.styl
@@ -1,5 +1,88 @@
@import colors
+.post-list
+ ul
+ list-style-type: none
+ margin: 0
+ padding: 0
+ display: flex
+ align-content: flex-end
+ flex-wrap: wrap
+ margin: 0 -0.25em
+
+ li
+ position: relative
+ flex-grow: 1
+ margin: 0 0.25em 0.5em 0.25em
+ display: inline-block
+ text-align: left
+ min-width: 10em
+ width: 15vw
+ &:not(.flexbox-dummy)
+ min-height: 7.5em
+ height: 11.25vw
+
+ a
+ display: inline-block
+ width: 100%
+ height: 100%
+ line-height: 100%
+ font-size: 80%
+ color: white
+
+ .type
+ position: absolute
+ left: -1px
+ bottom: -1px
+ padding: 1em 4em 0.5em 0.5em
+ background-image: radial-gradient(
+ ellipse 100% 100% at bottom left,
+ rgba(0,0,0,0.5),
+ rgba(0,0,0,0));
+ &[data-type=image]
+ display: none
+
+ .stats
+ position: absolute
+ right: -1px
+ bottom: -1px
+ text-align: right
+ /*padding: 0.5em
+ background: rgba(0,0,0,0.5);*/
+ padding: 1em 0.5em 0.5em 4em
+ background-image: radial-gradient(
+ ellipse 100% 100% at bottom right,
+ rgba(0,0,0,0.5),
+ rgba(0,0,0,0));
+ i
+ margin-right: 0.5em
+ .icon:not(:first-of-type)
+ margin-left: 1em
+
+ .thumbnail
+ border: 1px solid $inactive-link-color
+ background-position: 50% 30%
+ width: 100%
+ height: 100%
+ margin: 0 0.6em 0 0
+ &:hover
+ background: $main-color
+ .thumbnail
+ opacity: .9
+ border: 1px solid $main-color
+ outline: 2px solid $main-color
+
+.post-list-header
+ text-align: left
+ form
+ width: auto
+ input[name=search-text]
+ width: 25em
+ max-width: 90vw
+ .append
+ font-size: 0.95em
+ color: $inactive-link-color
+
.post-container
text-align: center
.post-content
diff --git a/client/css/users.styl b/client/css/users.styl
index 5759c16..ac4f68c 100644
--- a/client/css/users.styl
+++ b/client/css/users.styl
@@ -69,12 +69,11 @@
.user-list
ul
list-style-type: none
- margin: 0
padding: 0
display: flex
align-content: flex-end
flex-wrap: wrap
- margin: 0 -0.5em 0 -0.5em
+ margin: 0 -0.5em
li
flex-grow: 1
width: 20em
diff --git a/client/html/posts_header.tpl b/client/html/posts_header.tpl
new file mode 100644
index 0000000..5f301d6
--- /dev/null
+++ b/client/html/posts_header.tpl
@@ -0,0 +1,15 @@
+
diff --git a/client/html/posts_page.tpl b/client/html/posts_page.tpl
new file mode 100644
index 0000000..1518980
--- /dev/null
+++ b/client/html/posts_page.tpl
@@ -0,0 +1,39 @@
+
+ <% if (ctx.results.length) { %>
+
+ <% } %>
+
diff --git a/client/js/controllers/posts_controller.js b/client/js/controllers/posts_controller.js
index d3e31e3..54efb10 100644
--- a/client/js/controllers/posts_controller.js
+++ b/client/js/controllers/posts_controller.js
@@ -2,15 +2,24 @@
const misc = require('../util/misc.js');
const page = require('page');
+const api = require('../api.js');
const topNavController = require('../controllers/top_nav_controller.js');
+const pageController = require('../controllers/page_controller.js');
+const PostsHeaderView = require('../views/posts_header_view.js');
+const PostsPageView = require('../views/posts_page_view.js');
const EmptyView = require('../views/empty_view.js');
class PostsController {
+ constructor() {
+ this._postsHeaderView = new PostsHeaderView();
+ this._postsPageView = new PostsPageView();
+ }
+
registerRoutes() {
page('/upload', (ctx, next) => { this._uploadPostsRoute(); });
page('/posts/:query?',
(ctx, next) => { misc.parseSearchQueryRoute(ctx, next); },
- (ctx, next) => { this._listPostsRoute(); });
+ (ctx, next) => { this._listPostsRoute(ctx); });
page(
'/post/:id',
(ctx, next) => { this._showPostRoute(ctx.params.id); });
@@ -25,9 +34,23 @@ class PostsController {
this._emptyView.render();
}
- _listPostsRoute() {
+ _listPostsRoute(ctx) {
topNavController.activate('posts');
- this._emptyView.render();
+
+ pageController.run({
+ state: ctx.state,
+ requestPage: page => {
+ const text = ctx.searchQuery.text;
+ return api.get(
+ `/posts/?query=${text}&page=${page}&pageSize=40&_fields=` +
+ `id,type,tags,score,favoriteCount,commentCount,thumbnailUrl`);
+ },
+ clientUrl: '/posts/' + misc.formatSearchQuery({
+ text: ctx.searchQuery.text, page: '{page}'}),
+ searchQuery: ctx.searchQuery,
+ headerRenderer: this._postsHeaderView,
+ pageRenderer: this._postsPageView,
+ });
}
_showPostRoute(id) {
diff --git a/client/js/views/posts_header_view.js b/client/js/views/posts_header_view.js
new file mode 100644
index 0000000..07cea46
--- /dev/null
+++ b/client/js/views/posts_header_view.js
@@ -0,0 +1,41 @@
+'use strict';
+
+const page = require('page');
+const keyboard = require('../util/keyboard.js');
+const misc = require('../util/misc.js');
+const views = require('../util/views.js');
+const TagAutoCompleteControl =
+ require('../controls/tag_auto_complete_control.js');
+
+class PostsHeaderView {
+ constructor() {
+ this._template = views.getTemplate('posts-header');
+ }
+
+ render(ctx) {
+ const target = ctx.target;
+ const source = this._template(ctx);
+
+ const form = source.querySelector('form');
+ const searchTextInput = form.querySelector('[name=search-text]');
+
+ if (searchTextInput) {
+ new TagAutoCompleteControl(searchTextInput);
+ }
+
+ keyboard.bind('q', () => {
+ form.querySelector('input').focus();
+ });
+
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ const text = searchTextInput.value;
+ searchTextInput.blur();
+ page('/posts/' + misc.formatSearchQuery({text: text}));
+ });
+
+ views.showView(target, source);
+ }
+}
+
+module.exports = PostsHeaderView;
diff --git a/client/js/views/posts_page_view.js b/client/js/views/posts_page_view.js
new file mode 100644
index 0000000..4e0d9c8
--- /dev/null
+++ b/client/js/views/posts_page_view.js
@@ -0,0 +1,17 @@
+'use strict';
+
+const views = require('../util/views.js');
+
+class PostsPageView {
+ constructor() {
+ this._template = views.getTemplate('posts-page');
+ }
+
+ render(ctx) {
+ const target = ctx.target;
+ const source = this._template(ctx);
+ views.showView(target, source);
+ }
+}
+
+module.exports = PostsPageView;