diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 index 615949c23a8..fb690805c6a 100644 --- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 +++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 @@ -1,6 +1,7 @@ import DiscourseURL from "discourse/lib/url"; import Composer from "discourse/models/composer"; import { minimumOffset } from "discourse/lib/offset-calculator"; +import { ajax } from "discourse/lib/ajax"; const bindings = { "!": { postAction: "showFlags" }, @@ -32,6 +33,7 @@ const bindings = { "g p": { path: "/my/activity" }, "g m": { path: "/my/messages" }, "g d": { path: "/my/activity/drafts" }, + "g s": { handler: "goToFirstSuggestedTopic", anonymous: true }, home: { handler: "goToFirstPost", anonymous: true }, "command+up": { handler: "goToFirstPost", anonymous: true }, j: { handler: "selectDown", anonymous: true }, @@ -133,6 +135,26 @@ export default { Ember.run.later(() => $(".d-editor .quote").click(), 500); }, + goToFirstSuggestedTopic() { + const $el = $(".suggested-topics a.raw-topic-link:first"); + if ($el.length) { + $el.click(); + } else { + const controller = this.container.lookup("controller:topic"); + // Only the last page contains list of suggested topics. + const url = `/t/${controller.get("model.id")}/last.json`; + ajax(url).then(result => { + if (result.suggested_topics && result.suggested_topics.length > 0) { + const topic = controller.store.createRecord( + "topic", + result.suggested_topics[0] + ); + DiscourseURL.routeTo(topic.get("url")); + } + }); + } + }, + goToFirstPost() { this._jumpTo("jumpTop"); }, diff --git a/test/javascripts/acceptance/keyboard-shortcuts-test.js.es6 b/test/javascripts/acceptance/keyboard-shortcuts-test.js.es6 new file mode 100644 index 00000000000..ae77149d80c --- /dev/null +++ b/test/javascripts/acceptance/keyboard-shortcuts-test.js.es6 @@ -0,0 +1,64 @@ +import { acceptance } from "helpers/qunit-helpers"; +import DiscourseURL from "discourse/lib/url"; + +acceptance("Keyboard Shortcuts", { loggedIn: true }); + +test("go to first suggested topic", async assert => { + server.get("/t/27331/4.json", () => [ + 200, + { "Content-Type": "application/json" }, + {} + ]); + + server.get("/t/27331.json", () => [ + 200, + { "Content-Type": "application/json" }, + {} + ]); + + /* + * No suggested topics exist. + */ + + server.get("/t/9/last.json", () => [ + 200, + { "Content-Type": "application/json" }, + {} + ]); + + await visit("/t/this-is-a-test-topic/9"); + await keyEvent(document, "keypress", "g".charCodeAt(0)); + await keyEvent(document, "keypress", "s".charCodeAt(0)); + assert.equal(currentURL(), "/t/this-is-a-test-topic/9"); + + /* + * Suggested topics elements exist. + */ + + await visit("/t/internationalization-localization/280"); + await keyEvent(document, "keypress", "g".charCodeAt(0)); + await keyEvent(document, "keypress", "s".charCodeAt(0)); + assert.equal(currentURL(), "/t/polls-are-still-very-buggy/27331/4"); + + /* + * Suggested topic is returned by server. + */ + + server.get("/t/28830/last.json", () => [ + 200, + { "Content-Type": "application/json" }, + { + suggested_topics: [ + { + id: 27331, + slug: "keyboard-shortcuts-are-awesome" + } + ] + } + ]); + + await visit("/t/1-3-0beta9-no-rate-limit-popups/28830"); + await keyEvent(document, "keypress", "g".charCodeAt(0)); + await keyEvent(document, "keypress", "s".charCodeAt(0)); + assert.equal(currentURL(), "/t/keyboard-shortcuts-are-awesome/27331"); +}); diff --git a/test/javascripts/fixtures/topic.js.es6 b/test/javascripts/fixtures/topic.js.es6 index 1a71861e405..e0869104d4a 100644 --- a/test/javascripts/fixtures/topic.js.es6 +++ b/test/javascripts/fixtures/topic.js.es6 @@ -2321,222 +2321,6 @@ export default { post_count: 1 } ], - suggested_topics: [ - { - id: 27331, - title: "Polls are still very buggy", - fancy_title: "Polls are still very buggy", - slug: "polls-are-still-very-buggy", - posts_count: 4, - reply_count: 1, - highest_post_number: 4, - image_url: - "/uploads/default/_optimized/cd1/b8c/c162528887_690x401.png", - created_at: "2015-04-08T09:51:00.357Z", - last_posted_at: "2015-04-08T15:59:16.258Z", - bumped: true, - bumped_at: "2015-04-08T16:05:09.842Z", - unseen: false, - last_read_post_number: 3, - unread: 0, - new_posts: 1, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - notification_level: 2, - bookmarked: false, - liked: false, - archetype: "regular", - like_count: 11, - views: 55, - category_id: 1 - }, - { - id: 27343, - title: - "Mobile theme doesn't show last activity time for topics on category page", - fancy_title: - "Mobile theme doesn’t show last activity time for topics on category page", - slug: - "mobile-theme-doesnt-show-last-activity-time-for-topics-on-category-page", - posts_count: 4, - reply_count: 2, - highest_post_number: 4, - image_url: - "/uploads/default/_optimized/13e/25c/bd30b466be_281x500.png", - created_at: "2015-04-08T14:20:51.177Z", - last_posted_at: "2015-04-08T15:40:30.037Z", - bumped: true, - bumped_at: "2015-04-08T15:40:30.037Z", - unseen: false, - last_read_post_number: 2, - unread: 0, - new_posts: 2, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - notification_level: 2, - bookmarked: false, - liked: false, - archetype: "regular", - like_count: 3, - views: 23, - category_id: 9 - }, - { - id: 27346, - title: - 'Reply+{messagekey}@... optionaly in header "from" in addition to "reply-to"', - fancy_title: - "Reply+{messagekey}@… optionaly in header “from” in addition to “reply-to”", - slug: - "reply-messagekey-optionaly-in-header-from-in-addition-to-reply-to", - posts_count: 1, - reply_count: 0, - highest_post_number: 1, - image_url: null, - created_at: "2015-04-08T16:05:13.103Z", - last_posted_at: "2015-04-08T16:05:13.415Z", - bumped: true, - bumped_at: "2015-04-08T16:05:13.415Z", - unseen: true, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - bookmarked: null, - liked: null, - archetype: "regular", - like_count: 0, - views: 8, - category_id: 2 - }, - { - id: 19670, - title: "Parsing (Oneboxing) IMDB links", - fancy_title: "Parsing (Oneboxing) IMDB links", - slug: "parsing-oneboxing-imdb-links", - posts_count: 8, - reply_count: 1, - highest_post_number: 8, - image_url: null, - created_at: "2014-09-05T07:19:26.161Z", - last_posted_at: "2015-04-07T09:21:21.570Z", - bumped: true, - bumped_at: "2015-04-07T09:21:21.570Z", - unseen: false, - last_read_post_number: 8, - unread: 0, - new_posts: 0, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - notification_level: 2, - bookmarked: false, - liked: false, - archetype: "regular", - like_count: 4, - views: 253, - category_id: 2 - }, - { - id: 7512, - title: - "Support for Piwik Analytics as an alternative to Google Analytics", - fancy_title: - "Support for Piwik Analytics as an alternative to Google Analytics", - slug: - "support-for-piwik-analytics-as-an-alternative-to-google-analytics", - posts_count: 53, - reply_count: 41, - highest_post_number: 65, - image_url: "/plugins/emoji/images/smile.png", - created_at: "2013-06-16T01:32:30.596Z", - last_posted_at: "2015-02-22T13:46:26.845Z", - bumped: true, - bumped_at: "2015-02-22T13:46:26.845Z", - unseen: false, - last_read_post_number: 65, - unread: 0, - new_posts: 0, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - notification_level: 2, - bookmarked: false, - liked: false, - archetype: "regular", - like_count: 62, - views: 1877, - category_id: 2 - }, - { - id: 25480, - title: "CSS admin-contents reloaded", - fancy_title: "CSS admin-contents reloaded", - slug: "css-admin-contents-reloaded", - posts_count: 22, - reply_count: 15, - highest_post_number: 22, - image_url: null, - created_at: "2015-02-21T12:15:57.707Z", - last_posted_at: "2015-03-02T23:24:18.899Z", - bumped: true, - bumped_at: "2015-03-02T23:24:18.899Z", - unseen: false, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - bookmarked: null, - liked: null, - archetype: "regular", - like_count: 21, - views: 185, - category_id: 2 - }, - { - id: 26576, - title: "Badge timestamp should be the time the badge was granted?", - fancy_title: - "Badge timestamp should be the time the badge was granted?", - slug: "badge-timestamp-should-be-the-time-the-badge-was-granted", - posts_count: 2, - reply_count: 0, - highest_post_number: 2, - image_url: null, - created_at: "2015-03-20T13:22:08.266Z", - last_posted_at: "2015-03-21T00:33:52.243Z", - bumped: true, - bumped_at: "2015-03-21T00:33:52.243Z", - unseen: false, - last_read_post_number: 1, - unread: 0, - new_posts: 0, - pinned: false, - unpinned: null, - visible: true, - closed: false, - archived: false, - notification_level: 1, - bookmarked: false, - liked: false, - archetype: "regular", - like_count: 9, - views: 87, - category_id: 2 - } - ], links: [ { url: @@ -3140,6 +2924,220 @@ export default { ], chunk_size: 20, bookmarked: false, + suggested_topics: [ + { + id: 27331, + title: "Polls are still very buggy", + fancy_title: "Polls are still very buggy", + slug: "polls-are-still-very-buggy", + posts_count: 4, + reply_count: 1, + highest_post_number: 4, + image_url: "/uploads/default/_optimized/cd1/b8c/c162528887_690x401.png", + created_at: "2015-04-08T09:51:00.357Z", + last_posted_at: "2015-04-08T15:59:16.258Z", + bumped: true, + bumped_at: "2015-04-08T16:05:09.842Z", + unseen: false, + last_read_post_number: 3, + unread: 0, + new_posts: 1, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + notification_level: 2, + bookmarked: false, + liked: false, + archetype: "regular", + like_count: 11, + views: 55, + category_id: 1 + }, + { + id: 27343, + title: + "Mobile theme doesn't show last activity time for topics on category page", + fancy_title: + "Mobile theme doesn’t show last activity time for topics on category page", + slug: + "mobile-theme-doesnt-show-last-activity-time-for-topics-on-category-page", + posts_count: 4, + reply_count: 2, + highest_post_number: 4, + image_url: "/uploads/default/_optimized/13e/25c/bd30b466be_281x500.png", + created_at: "2015-04-08T14:20:51.177Z", + last_posted_at: "2015-04-08T15:40:30.037Z", + bumped: true, + bumped_at: "2015-04-08T15:40:30.037Z", + unseen: false, + last_read_post_number: 2, + unread: 0, + new_posts: 2, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + notification_level: 2, + bookmarked: false, + liked: false, + archetype: "regular", + like_count: 3, + views: 23, + category_id: 9 + }, + { + id: 27346, + title: + 'Reply+{messagekey}@... optionaly in header "from" in addition to "reply-to"', + fancy_title: + "Reply+{messagekey}@… optionaly in header “from” in addition to “reply-to”", + slug: + "reply-messagekey-optionaly-in-header-from-in-addition-to-reply-to", + posts_count: 1, + reply_count: 0, + highest_post_number: 1, + image_url: null, + created_at: "2015-04-08T16:05:13.103Z", + last_posted_at: "2015-04-08T16:05:13.415Z", + bumped: true, + bumped_at: "2015-04-08T16:05:13.415Z", + unseen: true, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + archetype: "regular", + like_count: 0, + views: 8, + category_id: 2 + }, + { + id: 19670, + title: "Parsing (Oneboxing) IMDB links", + fancy_title: "Parsing (Oneboxing) IMDB links", + slug: "parsing-oneboxing-imdb-links", + posts_count: 8, + reply_count: 1, + highest_post_number: 8, + image_url: null, + created_at: "2014-09-05T07:19:26.161Z", + last_posted_at: "2015-04-07T09:21:21.570Z", + bumped: true, + bumped_at: "2015-04-07T09:21:21.570Z", + unseen: false, + last_read_post_number: 8, + unread: 0, + new_posts: 0, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + notification_level: 2, + bookmarked: false, + liked: false, + archetype: "regular", + like_count: 4, + views: 253, + category_id: 2 + }, + { + id: 7512, + title: + "Support for Piwik Analytics as an alternative to Google Analytics", + fancy_title: + "Support for Piwik Analytics as an alternative to Google Analytics", + slug: + "support-for-piwik-analytics-as-an-alternative-to-google-analytics", + posts_count: 53, + reply_count: 41, + highest_post_number: 65, + image_url: "/plugins/emoji/images/smile.png", + created_at: "2013-06-16T01:32:30.596Z", + last_posted_at: "2015-02-22T13:46:26.845Z", + bumped: true, + bumped_at: "2015-02-22T13:46:26.845Z", + unseen: false, + last_read_post_number: 65, + unread: 0, + new_posts: 0, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + notification_level: 2, + bookmarked: false, + liked: false, + archetype: "regular", + like_count: 62, + views: 1877, + category_id: 2 + }, + { + id: 25480, + title: "CSS admin-contents reloaded", + fancy_title: "CSS admin-contents reloaded", + slug: "css-admin-contents-reloaded", + posts_count: 22, + reply_count: 15, + highest_post_number: 22, + image_url: null, + created_at: "2015-02-21T12:15:57.707Z", + last_posted_at: "2015-03-02T23:24:18.899Z", + bumped: true, + bumped_at: "2015-03-02T23:24:18.899Z", + unseen: false, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + bookmarked: null, + liked: null, + archetype: "regular", + like_count: 21, + views: 185, + category_id: 2 + }, + { + id: 26576, + title: "Badge timestamp should be the time the badge was granted?", + fancy_title: + "Badge timestamp should be the time the badge was granted?", + slug: "badge-timestamp-should-be-the-time-the-badge-was-granted", + posts_count: 2, + reply_count: 0, + highest_post_number: 2, + image_url: null, + created_at: "2015-03-20T13:22:08.266Z", + last_posted_at: "2015-03-21T00:33:52.243Z", + bumped: true, + bumped_at: "2015-03-21T00:33:52.243Z", + unseen: false, + last_read_post_number: 1, + unread: 0, + new_posts: 0, + pinned: false, + unpinned: null, + visible: true, + closed: false, + archived: false, + notification_level: 1, + bookmarked: false, + liked: false, + archetype: "regular", + like_count: 9, + views: 87, + category_id: 2 + } + ], tags: null }, "/t/28830/1.json": {