mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:42:02 +08:00
FEATURE: g,j and g,k to navigate to next and prev topic
After visiting a topic list (by tag / category / top level) we track the list Once a list is tracked the combo `g` `j` can be used to go to the next topic in the list and `g` `k` to go to previous topic. This allows you to quickly work through subsets of topics without having to navigate back to the top level lists The shortcut does not work in PM lists yet, or search results, both are under consideration.
This commit is contained in:
parent
6da90af6c6
commit
dc14d156b6
|
@ -69,7 +69,9 @@ export default Controller.extend(ModalFunctionality, {
|
|||
bookmarks: buildShortcut("jump_to.bookmarks", { keys1: ["g", "b"] }),
|
||||
profile: buildShortcut("jump_to.profile", { keys1: ["g", "p"] }),
|
||||
messages: buildShortcut("jump_to.messages", { keys1: ["g", "m"] }),
|
||||
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] })
|
||||
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] }),
|
||||
next: buildShortcut("jump_to.next", { keys1: ["g", "j"] }),
|
||||
previous: buildShortcut("jump_to.previous", { keys1: ["g", "k"] })
|
||||
},
|
||||
navigation: {
|
||||
back: buildShortcut("navigation.back", { keys1: ["u"] }),
|
||||
|
|
|
@ -5,6 +5,10 @@ import { minimumOffset } from "discourse/lib/offset-calculator";
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { throttle, schedule } from "@ember/runloop";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import {
|
||||
nextTopicUrl,
|
||||
previousTopicUrl
|
||||
} from "discourse/lib/topic-list-tracker";
|
||||
|
||||
const DEFAULT_BINDINGS = {
|
||||
"!": { postAction: "showFlags" },
|
||||
|
@ -36,6 +40,8 @@ const DEFAULT_BINDINGS = {
|
|||
"g m": { path: "/my/messages" },
|
||||
"g d": { path: "/my/activity/drafts" },
|
||||
"g s": { handler: "goToFirstSuggestedTopic", anonymous: true },
|
||||
"g j": { handler: "goToNextTopic", anonymous: true },
|
||||
"g k": { handler: "goToPreviousTopic", anonymous: true },
|
||||
home: { handler: "goToFirstPost", anonymous: true },
|
||||
"command+up": { handler: "goToFirstPost", anonymous: true },
|
||||
j: { handler: "selectDown", anonymous: true },
|
||||
|
@ -228,6 +234,22 @@ export default {
|
|||
return false;
|
||||
},
|
||||
|
||||
goToNextTopic() {
|
||||
nextTopicUrl().then(url => {
|
||||
if (url) {
|
||||
DiscourseURL.routeTo(url);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
goToPreviousTopic() {
|
||||
previousTopicUrl().then(url => {
|
||||
if (url) {
|
||||
DiscourseURL.routeTo(url);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
goToFirstSuggestedTopic() {
|
||||
const $el = $(".suggested-topics a.raw-topic-link:first");
|
||||
if ($el.length) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import { Promise } from "rsvp";
|
||||
let model, currentTopicId;
|
||||
|
||||
export function setTopicList(incomingModel) {
|
||||
model = incomingModel;
|
||||
currentTopicId = null;
|
||||
}
|
||||
|
||||
export function nextTopicUrl() {
|
||||
return urlAt(1);
|
||||
}
|
||||
|
||||
export function previousTopicUrl() {
|
||||
return urlAt(-1);
|
||||
}
|
||||
|
||||
function urlAt(delta) {
|
||||
if (!model || !model.topics) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
let index = currentIndex();
|
||||
if (index === -1) {
|
||||
index = 0;
|
||||
} else {
|
||||
index += delta;
|
||||
}
|
||||
|
||||
const topic = model.topics[index];
|
||||
|
||||
if (!topic && index > 0 && model.more_topics_url && model.loadMore) {
|
||||
return model.loadMore().then(() => urlAt(delta));
|
||||
}
|
||||
|
||||
if (topic) {
|
||||
currentTopicId = topic.id;
|
||||
return Promise.resolve(topic.lastUnreadUrl);
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
export function setTopicId(topicId) {
|
||||
currentTopicId = topicId;
|
||||
}
|
||||
|
||||
function currentIndex() {
|
||||
if (currentTopicId && model && model.topics) {
|
||||
const idx = _.findIndex(model.topics, t => t.id === currentTopicId);
|
||||
if (idx > -1) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -6,6 +6,7 @@ import DiscourseRoute from "discourse/routes/discourse";
|
|||
import OpenComposer from "discourse/mixins/open-composer";
|
||||
import { scrollTop } from "discourse/mixins/scroll-top";
|
||||
import User from "discourse/models/user";
|
||||
import { setTopicList } from "discourse/lib/topic-list-tracker";
|
||||
|
||||
export default DiscourseRoute.extend(OpenComposer, {
|
||||
queryParams: {
|
||||
|
@ -46,6 +47,9 @@ export default DiscourseRoute.extend(OpenComposer, {
|
|||
didTransition() {
|
||||
this.controllerFor("discovery")._showFooter();
|
||||
this.send("loadingComplete");
|
||||
|
||||
const model = this.controllerFor("discovery/topics").get("model");
|
||||
setTopicList(model);
|
||||
return false;
|
||||
},
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import PermissionType from "discourse/models/permission-type";
|
|||
import Category from "discourse/models/category";
|
||||
import FilterModeMixin from "discourse/mixins/filter-mode";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { setTopicList } from "discourse/lib/topic-list-tracker";
|
||||
|
||||
export default DiscourseRoute.extend(FilterModeMixin, {
|
||||
navMode: "latest",
|
||||
|
@ -99,6 +100,9 @@ export default DiscourseRoute.extend(FilterModeMixin, {
|
|||
staff: list.topic_list.tags[0].staff
|
||||
});
|
||||
}
|
||||
|
||||
setTopicList(list);
|
||||
|
||||
controller.setProperties({
|
||||
list,
|
||||
canCreateTopic: list.get("can_create_topic"),
|
||||
|
|
|
@ -4,6 +4,7 @@ import { cancel, later, schedule } from "@ember/runloop";
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { ID_CONSTRAINT } from "discourse/models/topic";
|
||||
import { setTopicId } from "discourse/lib/topic-list-tracker";
|
||||
|
||||
const SCROLL_DELAY = 500;
|
||||
|
||||
|
@ -200,7 +201,10 @@ const TopicRoute = DiscourseRoute.extend({
|
|||
},
|
||||
|
||||
didTransition() {
|
||||
this.controllerFor("topic")._showFooter();
|
||||
const controller = this.controllerFor("topic");
|
||||
controller._showFooter();
|
||||
const topicId = controller.get("model.id");
|
||||
setTopicId(topicId);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
<li>{{html-safe shortcuts.jump_to.messages}}</li>
|
||||
{{/if}}
|
||||
<li>{{html-safe shortcuts.jump_to.drafts}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.next}}</li>
|
||||
<li>{{html-safe shortcuts.jump_to.previous}}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
|
|
|
@ -3185,6 +3185,8 @@ en:
|
|||
profile: "%{shortcut} Profile"
|
||||
messages: "%{shortcut} Messages"
|
||||
drafts: "%{shortcut} Drafts"
|
||||
next: "%{shortcut} Next Topic"
|
||||
previous: "%{shortcut} Previous Topic"
|
||||
navigation:
|
||||
title: "Navigation"
|
||||
jump: "%{shortcut} Go to post #"
|
||||
|
|
Loading…
Reference in New Issue
Block a user