diff --git a/TODO b/TODO index 0b81f952..eec316a8 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,6 @@ everything related to posts: - single post view - basic information - time of last edit - - time of last feature - - how many times the post was featured - - feature - fav - score (see notes about scoring) - editing @@ -30,11 +27,9 @@ everything related to posts: - hiding posts - link to iqdb and/or tineye - - featured posts - - ability to feature the post - - show the post on main page - - tag list - - uploader + - auto feature + - number of posts + - total size of posts - random post - regard safety settings @@ -115,7 +110,6 @@ everything related to comments: - score (see notes about scoring) refactors: - - centralize markdown prefix decorators - add enum validation in IValidatables (needs refactors of enums and possible disposal of EnumHelper in favor of something more subtle) - (idea) keep denormalized data in separate tables, i.e. tag usages in diff --git a/data/config.ini b/data/config.ini index c6fb52b9..d1551c18 100644 --- a/data/config.ini +++ b/data/config.ini @@ -42,6 +42,7 @@ listUnsafePosts = anonymous, regularUser, powerUser, moderator, administ uploadPosts = regularUser, powerUser, moderator, administrator uploadPostsAnonymously = regularUser, powerUser, moderator, administrator deletePosts = moderator, administrator +featurePosts = moderator, administrator listTags = anonymous, regularUser, powerUser, moderator, administrator diff --git a/public_html/css/home.css b/public_html/css/home.css new file mode 100644 index 00000000..cf52b012 --- /dev/null +++ b/public_html/css/home.css @@ -0,0 +1,29 @@ +#home { + text-align: center; +} +#home h1 { + margin-top: 0; +} + +#home .post { + text-align: left; + margin: 0 auto; + max-width: 60%; +} +#home .post .left { + display: inline-block; + float: left; +} +#home .post .right { + display: inline-block; + float: right; +} + +#home .post-footer, +#home .post-footer img { + vertical-align: middle; +} + +#home .post-footer img { + margin-left: 0.5em; +} diff --git a/public_html/index.html b/public_html/index.html index da2cef63..86a5b4cd 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -31,6 +31,7 @@ + diff --git a/public_html/js/Auth.js b/public_html/js/Auth.js index a10598cd..bc0ca76b 100644 --- a/public_html/js/Auth.js +++ b/public_html/js/Auth.js @@ -24,6 +24,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) { uploadPosts: 'uploadPosts', uploadPostsAnonymously: 'uploadPostsAnonymously', deletePosts: 'deletePosts', + featurePosts: 'featurePosts', listTags: 'listTags', }; diff --git a/public_html/js/Presenters/HomePresenter.js b/public_html/js/Presenters/HomePresenter.js index 6f2b1254..ebb1e38e 100644 --- a/public_html/js/Presenters/HomePresenter.js +++ b/public_html/js/Presenters/HomePresenter.js @@ -2,20 +2,50 @@ var App = App || {}; App.Presenters = App.Presenters || {}; App.Presenters.HomePresenter = function( + _, jQuery, - topNavigationPresenter) { + util, + promise, + api, + topNavigationPresenter, + messagePresenter) { var $el = jQuery('#content'); + var homeTemplate; + var postContentTemplate; + var post; function init(args, loaded) { topNavigationPresenter.select('home'); topNavigationPresenter.changeTitle('Home'); - render(); - loaded(); + + promise.waitAll( + util.promiseTemplate('home'), + util.promiseTemplate('post-content'), + api.get('/posts/featured')) + .then(function( + homeTemplateHtml, + postContentTemplateHtml, + response) { + homeTemplate = _.template(homeTemplateHtml); + postContentTemplate = _.template(postContentTemplateHtml); + + post = response.json; + render(); + loaded(); + + }).fail(function(response) { + messagePresenter.showError($el, response.json && response.json.error || response); + }); } function render() { - $el.html('Home placeholder'); + $el.html(homeTemplate({ + post: post, + postContentTemplate: postContentTemplate, + title: topNavigationPresenter.getBaseTitle(), + formatRelativeTime: util.formatRelativeTime, + })); } return { @@ -25,4 +55,4 @@ App.Presenters.HomePresenter = function( }; -App.DI.register('homePresenter', ['jQuery', 'topNavigationPresenter'], App.Presenters.HomePresenter); +App.DI.register('homePresenter', ['_', 'jQuery', 'util', 'promise', 'api', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.HomePresenter); diff --git a/public_html/js/Presenters/PostPresenter.js b/public_html/js/Presenters/PostPresenter.js index a042b55c..0efa840a 100644 --- a/public_html/js/Presenters/PostPresenter.js +++ b/public_html/js/Presenters/PostPresenter.js @@ -25,6 +25,7 @@ App.Presenters.PostPresenter = function( topNavigationPresenter.select('posts'); privileges.canDeletePosts = auth.hasPrivilege(auth.privileges.deletePosts); + privileges.canFeaturePosts = auth.hasPrivilege(auth.privileges.featurePosts); promise.waitAll( util.promiseTemplate('post'), @@ -34,7 +35,6 @@ App.Presenters.PostPresenter = function( postTemplateHtml, postContentTemplateHtml, response) { - $messages = $el.find('.messages'); postTemplate = _.template(postTemplateHtml); postContentTemplate = _.template(postContentTemplateHtml); @@ -45,7 +45,7 @@ App.Presenters.PostPresenter = function( }).fail(function(response) { $el.empty(); - messagePresenter.showError($messages, response.json && response.json.error || response); + showGenericError(response); }); } @@ -57,23 +57,45 @@ App.Presenters.PostPresenter = function( postContentTemplate: postContentTemplate, privileges: privileges, })); + $messages = $el.find('.messages'); $el.find('.delete').click(deleteButtonClicked); + $el.find('.feature').click(featureButtonClicked); } function deleteButtonClicked(e) { e.preventDefault(); + messagePresenter.hideMessages($messages); if (window.confirm('Do you really want to delete this post?')) { deletePost(); } } function deletePost() { - api.delete('/posts/' + post.id).then(function(response) { - router.navigate('#/posts'); - }).fail(function(response) { - messagePresenter.showError($messages, response.json && response.json.error || response); - }); + api.delete('/posts/' + post.id) + .then(function(response) { + router.navigate('#/posts'); + }).fail(showGenericError); + } + + function featureButtonClicked(e) { + e.preventDefault(); + messagePresenter.hideMessages($messages); + if (window.confirm('Do you want to feature this post on fron page?')) { + featurePost(); + } + } + + function featurePost() { + api.post('/posts/' + post.id + '/feature') + .then(function(response) { + router.navigate('#/home'); + }) + .fail(showGenericError); + } + + function showGenericError(response) { + messagePresenter.showError($messages, response.json && response.json.error || response); } return { diff --git a/public_html/js/Presenters/TopNavigationPresenter.js b/public_html/js/Presenters/TopNavigationPresenter.js index 44d3b719..5b99f970 100644 --- a/public_html/js/Presenters/TopNavigationPresenter.js +++ b/public_html/js/Presenters/TopNavigationPresenter.js @@ -47,6 +47,10 @@ App.Presenters.TopNavigationPresenter = function( $el.find('li.' + selectedElement).find('a').addClass('active'); } + function getBaseTitle() { + return baseTitle; + } + function changeTitle(subTitle) { var newTitle = baseTitle; if (subTitle) { @@ -59,6 +63,7 @@ App.Presenters.TopNavigationPresenter = function( init: init, render: render, select: select, + getBaseTitle: getBaseTitle, changeTitle: changeTitle, }; diff --git a/public_html/templates/home.tpl b/public_html/templates/home.tpl new file mode 100644 index 00000000..4806bcad --- /dev/null +++ b/public_html/templates/home.tpl @@ -0,0 +1,30 @@ +