From f8e6d07feac0927f62b88b5bf00662ad5cfbdcf7 Mon Sep 17 00:00:00 2001 From: rr- Date: Thu, 2 Jun 2016 00:07:51 +0200 Subject: [PATCH] client/posts: add proof of concept for post list --- client/css/posts.styl | 83 +++++++++++++++++++++++ client/css/users.styl | 3 +- client/html/posts_header.tpl | 15 ++++ client/html/posts_page.tpl | 39 +++++++++++ client/js/controllers/posts_controller.js | 29 +++++++- client/js/views/posts_header_view.js | 41 +++++++++++ client/js/views/posts_page_view.js | 17 +++++ 7 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 client/html/posts_header.tpl create mode 100644 client/html/posts_page.tpl create mode 100644 client/js/views/posts_header_view.js create mode 100644 client/js/views/posts_page_view.js 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 @@ +
+
+
+
    +
  • + <%= ctx.makeTextInput({id: 'search-text', name: 'search-text', value: ctx.searchQuery.text}) %> +
  • +
+
+
+ + Syntax help +
+
+
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;