diff --git a/tests/fixtures/api/issues/396.json b/tests/fixtures/api/issues/396.json new file mode 100644 index 000000000..11e55f8c0 --- /dev/null +++ b/tests/fixtures/api/issues/396.json @@ -0,0 +1,48 @@ +{ + "_fixture": true, + "url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/396", + "repository_url": "https://api.github.com/repos/webcompat/webcompat-tests", + "labels_url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/396/labels{/name}", + "comments_url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/396/comments", + "events_url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/396/events", + "html_url": "https://github.com/webcompat/webcompat-tests/issues/396", + "id": 155848972, + "number": 396, + "title": "s.yimg.com - mobile site is not usable (note this issue requires a nsfw label for tests, don't remove)", + "user": { + "login": "miketaylr", + "id": 67283, + "avatar_url": "https://avatars.githubusercontent.com/u/67283?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/miketaylr", + "html_url": "https://github.com/miketaylr", + "followers_url": "https://api.github.com/users/miketaylr/followers", + "following_url": "https://api.github.com/users/miketaylr/following{/other_user}", + "gists_url": "https://api.github.com/users/miketaylr/gists{/gist_id}", + "starred_url": "https://api.github.com/users/miketaylr/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/miketaylr/subscriptions", + "organizations_url": "https://api.github.com/users/miketaylr/orgs", + "repos_url": "https://api.github.com/users/miketaylr/repos", + "events_url": "https://api.github.com/users/miketaylr/events{/privacy}", + "received_events_url": "https://api.github.com/users/miketaylr/received_events", + "type": "User", + "site_admin": false + }, + "labels": [ + { + "url": "https://api.github.com/repos/webcompat/webcompat-tests/labels/nsfw", + "name": "nsfw", + "color": "b60205" + } + ], + "state": "open", + "locked": false, + "assignee": null, + "milestone": null, + "comments": 1, + "created_at": "2016-05-19T22:23:40Z", + "updated_at": "2016-06-10T15:11:51Z", + "closed_at": null, + "body": "\n\n\n**URL**: https://s.yimg.com/lo/api/res/1.2/hV2VUJUemnMng439Rbbitw--/YXBwaWQ9eWlzZWFyY2g7Zmk9Zml0O2dlPTAwNjYwMDtncz0wMEEzMDA7aD00MDA7dz03MTE-/http://www.educadictos.com/wp-content/uploads/2013/06/color.png.cf.png\n**Browser / Version**: Firefox Mobile 48.0\n**Operating System**: Android 6.0.1\n**Problem type**: Mobile site is not usable\n\n**Steps to Reproduce**\n1) Navigate to: https://s.yimg.com/lo/api/res/1.2/hV2VUJUemnMng439Rbbitw--/YXBwaWQ9eWlzZWFyY2g7Zmk9Zml0O2dlPTAwNjYwMDtncz0wMEEzMDA7aD00MDA7dz03MTE-/http://www.educadictos.com/wp-content/uploads/2013/06/color.png.cf.png\r\n2) …\r\n\r\nExpected Behavior:\r\n\r\nActual Behavior:\r\n\r\n\r\n![Screenshot Description](http://staging.webcompat.com/uploads/2016/5/02f7720b-000f-49fd-9c0c-2e67a843b405.jpeg)\n\n\n\n_From [webcompat.com](https://webcompat.com/) with ❤️_", + "closed_by": null +} diff --git a/tests/fixtures/api/issues/396/comments.6665c4820456e6a813dcbbdddd690ffc.json b/tests/fixtures/api/issues/396/comments.6665c4820456e6a813dcbbdddd690ffc.json new file mode 100644 index 000000000..e0b2cd194 --- /dev/null +++ b/tests/fixtures/api/issues/396/comments.6665c4820456e6a813dcbbdddd690ffc.json @@ -0,0 +1,31 @@ +[ + { + "_fixture": true, + "url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/comments/225209757", + "html_url": "https://github.com/webcompat/webcompat-tests/issues/396#issuecomment-225209757", + "issue_url": "https://api.github.com/repos/webcompat/webcompat-tests/issues/396", + "id": 225209757, + "user": { + "login": "miketaylr", + "id": 67283, + "avatar_url": "https://avatars.githubusercontent.com/u/67283?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/miketaylr", + "html_url": "https://github.com/miketaylr", + "followers_url": "https://api.github.com/users/miketaylr/followers", + "following_url": "https://api.github.com/users/miketaylr/following{/other_user}", + "gists_url": "https://api.github.com/users/miketaylr/gists{/gist_id}", + "starred_url": "https://api.github.com/users/miketaylr/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/miketaylr/subscriptions", + "organizations_url": "https://api.github.com/users/miketaylr/orgs", + "repos_url": "https://api.github.com/users/miketaylr/repos", + "events_url": "https://api.github.com/users/miketaylr/events{/privacy}", + "received_events_url": "https://api.github.com/users/miketaylr/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2016-06-10T15:11:51Z", + "updated_at": "2016-06-10T15:11:51Z", + "body": "![29upbiv](https://cloud.githubusercontent.com/assets/67283/15969039/bd82fae2-2ef3-11e6-800f-535785681b50.jpg)\r\n" + } +] diff --git a/tests/functional/issues-non-auth.js b/tests/functional/issues-non-auth.js index 2a78a21fd..d32e2991d 100644 --- a/tests/functional/issues-non-auth.js +++ b/tests/functional/issues-non-auth.js @@ -21,7 +21,7 @@ define([ return this.remote .setFindTimeout(intern.config.wc.pageLoadTimeout) .get(require.toUrl(url('/issues/100'))) - .sleep(1000) + .sleep(2000) .findByCssSelector('h1.js-Issue-title').getVisibleText() .then(function(text) { assert.include(text, 'Issue 100:', 'Issue title displayed'); @@ -151,6 +151,43 @@ define([ assert.equal(isDisplayed, false); }) .end(); + }, + + 'NSFW images are blurred': function() { + return this.remote + .setFindTimeout(intern.config.wc.pageLoadTimeout) + .get(require.toUrl(url('/issues/396'))) + .sleep(1000) + .findByCssSelector('.js-Issue-commentList .js-Comment-content p').getAttribute('class') + .then(function(className) { + assert.include(className, 'wc-Comment-content-nsfw'); + }) + .end(); + }, + + 'Clicking NSFW images toggles between blurry and not-blurry': function() { + return this.remote + .setFindTimeout(intern.config.wc.pageLoadTimeout) + .get(require.toUrl(url('/issues/396'))) + .sleep(1000) + .findByCssSelector('.js-Issue-commentList .js-Comment-content p').getAttribute('class') + .then(function(className) { + assert.include(className, 'wc-Comment-content-nsfw'); + assert.notInclude(className, 'wc-Comment-content-nsfw--display'); + }).click() + .end() + .findByCssSelector('.js-Issue-commentList .js-Comment-content p').getAttribute('class') + .then(function(className) { + assert.include(className, 'wc-Comment-content-nsfw'); + assert.include(className, 'wc-Comment-content-nsfw--display'); + }).click() + .end() + .findByCssSelector('.js-Issue-commentList .js-Comment-content p').getAttribute('class') + .then(function(className) { + assert.include(className, 'wc-Comment-content-nsfw'); + assert.notInclude(className, 'wc-Comment-content-nsfw--display'); + }) + .end(); } }); }); diff --git a/webcompat/static/css/development/components/markdown.css b/webcompat/static/css/development/components/markdown.css index f32ef8bef..815598db8 100644 --- a/webcompat/static/css/development/components/markdown.css +++ b/webcompat/static/css/development/components/markdown.css @@ -4,6 +4,8 @@ --Markdown-fontSize: 1rem; --Markdown-color: #000; --Markdown-colorHover: var(--base-colorDark); + --Markdown-bgColorNSFW: var(--base-colorDark); + --Markdown-colorNSFW: var(--base-background); } .wc-Markdown { @@ -39,6 +41,36 @@ max-height: 100%; } + .wc-Markdown .wc-Comment-content-nsfw { + position: relative; + } + + .wc-Markdown .wc-Comment-content-nsfw::after { + display: block; + content: "Click to show image"; + cursor: pointer; + text-align: center; + color: var(--Markdown-colorNSFW); + background: var(--Markdown-bgColorNSFW); + width: 50%; + padding: 1em; + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%, -50%); + } + + .wc-Markdown .wc-Comment-content-nsfw img { + filter: blur(50px); + } + + .wc-Markdown .wc-Comment-content-nsfw--display img { + filter: blur(0); + } + + .wc-Markdown .wc-Comment-content-nsfw--display::after { + display: none; + } + .wc-Markdown code { font-family: monospace; font-size: 90%; diff --git a/webcompat/static/js/lib/issues.js b/webcompat/static/js/lib/issues.js index 849dc9ae9..46a86a7dc 100644 --- a/webcompat/static/js/lib/issues.js +++ b/webcompat/static/js/lib/issues.js @@ -131,8 +131,11 @@ issues.MetaDataView = Backbone.View.extend({ issues.BodyView = Backbone.View.extend({ el: $('.wc-Issue-report'), + mainView: null, template: _.template($('#issue-info-tmpl').html()), - initialize: function() { + initialize: function(options) { + this.mainView = options.mainView; + this.QrView = new issues.QrView({ model: this.model }); @@ -140,6 +143,7 @@ issues.BodyView = Backbone.View.extend({ render: function() { this.$el.html(this.template(this.model.toJSON())); // hide metadata + var issueDesc = $('.js-Issue-markdown'); issueDesc .contents() @@ -155,6 +159,10 @@ issues.BodyView = Backbone.View.extend({ .parent() .addClass('is-hidden'); + if (this.mainView._isNSFW) { + issueDesc.find('img').closest('p').addClass('wc-Comment-content-nsfw'); + } + this.QrView.setElement('.wc-QrCode').render(); return this; } @@ -351,12 +359,14 @@ issues.MainView = Backbone.View.extend({ el: $('.js-Issue'), events: { 'click .js-Issue-comment-button': 'addNewComment', - 'click': 'closeLabelEditor' + 'click': 'closeLabelEditor', + 'click .wc-Comment-content-nsfw': 'toggleNSFW' }, keyboardEvents: { 'g': 'githubWarp' }, _supportsFormData: 'FormData' in window, + _isNSFW: undefined, initialize: function() { $(document.body).addClass('language-html'); var issueNum = {number: issueNumber}; @@ -386,7 +396,7 @@ issues.MainView = Backbone.View.extend({ var issueModel = {model: this.issue}; this.title = new issues.TitleView(issueModel); this.metadata = new issues.MetaDataView(issueModel); - this.body = new issues.BodyView(issueModel); + this.body = new issues.BodyView(_.extend(issueModel, {mainView: this})); this.labels = new issues.LabelsView(issueModel); this.textArea = new issues.TextAreaView(); this.imageUpload = new issues.ImageUploadView(); @@ -395,7 +405,12 @@ issues.MainView = Backbone.View.extend({ fetchModels: function() { var headersBag = {headers: {'Accept': 'application/json'}}; this.issue.fetch(headersBag).success(_.bind(function() { - _.each([this.title, this.metadata, this.body, this.labels, + // _.find() will return the object if found (which is truthy), + // or undefined if not found (which is falsey) + this._isNSFW = !!_.find(this.issue.get('labels'), + _.matchesProperty('name', 'nsfw')); + + _.each([this.title, this.metadata, this.labels, this.body, this.stateButton, this], function(elm) { elm.render(); @@ -413,6 +428,7 @@ issues.MainView = Backbone.View.extend({ if (this.issue.get('commentNumber') > 0) { this.comments.fetch(headersBag).success(_.bind(function() { this.addExistingComments(); + // the add event is fired when a model is added to the collection. this.comments.bind('add', _.bind(this.addComment, this)); // If there's a #hash pointing to a comment (or elsewhere) @@ -438,12 +454,19 @@ issues.MainView = Backbone.View.extend({ }); }, addComment: function(comment) { + // if there's a nsfw label, add the whatever class. var view = new issues.CommentView({model: comment}); - var commentElm = view.render().el; + var commentElm = view.render().$el; $('.js-Issue-commentList').append(commentElm); - _.each($(commentElm).find('code'), function(elm) { + _.each(commentElm.find('code'), function(elm) { Prism.highlightElement(elm); }); + + if (this._isNSFW) { + _.each(commentElm.find('img'), function(elm) { + $(elm).closest('p').addClass('wc-Comment-content-nsfw'); + }); + } }, addNewComment: function() { var form = $('.js-Comment-form'); @@ -468,6 +491,15 @@ issues.MainView = Backbone.View.extend({ addExistingComments: function() { this.comments.each(this.addComment, this); }, + toggleNSFW: function(e) { + // make sure we've got a reference to the element, + // (small images won't extend to the width of the containing + // p.nsfw) + var target = e.target.nodeName === 'IMG' ? + e.target : + e.target.nodeName === 'P' && e.target.firstElementChild; + $(target).parent().toggleClass('wc-Comment-content-nsfw--display'); + }, render: function() { this.$el.fadeIn(); }