FEATURE: allows to jump to a date in a topic
This commit is contained in:
parent
7f7944bc3a
commit
a2281fbb19
|
@ -94,6 +94,10 @@ export default Ember.Component.extend(PanEvents, {
|
|||
if (this.get("info.topicProgressExpanded")) {
|
||||
$(".timeline-fullscreen").removeClass("show");
|
||||
Ember.run.later(() => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("info.topicProgressExpanded", false);
|
||||
this._checkSize();
|
||||
}, 500);
|
||||
|
@ -102,11 +106,13 @@ export default Ember.Component.extend(PanEvents, {
|
|||
|
||||
keyboardTrigger(e) {
|
||||
if (e.type === "jump") {
|
||||
const controller = showModal("jump-to-post");
|
||||
const controller = showModal("jump-to-post", {
|
||||
modalClass: "jump-to-post-modal"
|
||||
});
|
||||
controller.setProperties({
|
||||
topic: this.get("topic"),
|
||||
postNumber: 1,
|
||||
jumpToIndex: this.attrs.jumpToIndex
|
||||
jumpToIndex: this.attrs.jumpToIndex,
|
||||
jumpToDate: this.attrs.jumpToDate
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,21 +3,41 @@ import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
model: null,
|
||||
postNumber: null,
|
||||
postDate: null,
|
||||
filteredPostsCount: Ember.computed.alias(
|
||||
"topic.postStream.filteredPostsCount"
|
||||
),
|
||||
|
||||
onShow: () => {
|
||||
onShow() {
|
||||
Ember.run.next(() => $("#post-jump").focus());
|
||||
},
|
||||
|
||||
actions: {
|
||||
jump() {
|
||||
const max = this.get("topic.postStream.filteredPostsCount");
|
||||
const where = Math.min(
|
||||
max,
|
||||
Math.max(1, parseInt(this.get("postNumber")))
|
||||
);
|
||||
|
||||
this.jumpToIndex(where);
|
||||
this.send("closeModal");
|
||||
if (this.get("postNumber")) {
|
||||
this._jumpToIndex(
|
||||
this.get("filteredPostsCount"),
|
||||
this.get("postNumber")
|
||||
);
|
||||
} else if (this.get("postDate")) {
|
||||
this._jumpToDate(this.get("postDate"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_jumpToIndex(postsCounts, postNumber) {
|
||||
const where = Math.min(postsCounts, Math.max(1, parseInt(postNumber)));
|
||||
this.jumpToIndex(where);
|
||||
this._close();
|
||||
},
|
||||
|
||||
_jumpToDate(date) {
|
||||
this.jumpToDate(date);
|
||||
this._close();
|
||||
},
|
||||
|
||||
_close() {
|
||||
this.setProperties({ postNumber: null, postDate: null });
|
||||
this.send("closeModal");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -575,15 +575,20 @@ export default Ember.Controller.extend(BufferedContent, {
|
|||
this._jumpToIndex(index);
|
||||
},
|
||||
|
||||
jumpToDate(date) {
|
||||
this._jumpToDate(date);
|
||||
},
|
||||
|
||||
jumpToPostPrompt() {
|
||||
const topic = this.get("model");
|
||||
const controller = showModal("jump-to-post");
|
||||
const controller = showModal("jump-to-post", {
|
||||
modalClass: "jump-to-post-modal"
|
||||
});
|
||||
controller.setProperties({
|
||||
topic: topic,
|
||||
topic,
|
||||
postNumber: null,
|
||||
jumpToIndex: index => {
|
||||
this.send("jumpToIndex", index);
|
||||
}
|
||||
jumpToIndex: index => this.send("jumpToIndex", index),
|
||||
jumpToDate: date => this.send("jumpToDate", date)
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -940,6 +945,21 @@ export default Ember.Controller.extend(BufferedContent, {
|
|||
}
|
||||
},
|
||||
|
||||
_jumpToDate(date) {
|
||||
const postStream = this.get("model.postStream");
|
||||
|
||||
postStream
|
||||
.loadNearestPostToDate(date)
|
||||
.then(post => {
|
||||
DiscourseURL.routeTo(
|
||||
this.get("model").urlForPostNumber(post.get("post_number"))
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
this._jumpToIndex(postStream.get("topic.highest_post_number"));
|
||||
});
|
||||
},
|
||||
|
||||
_jumpToPostNumber(postNumber) {
|
||||
const postStream = this.get("model.postStream");
|
||||
const post = postStream.get("posts").findBy("post_number", postNumber);
|
||||
|
|
|
@ -565,6 +565,15 @@ export default RestModel.extend({
|
|||
});
|
||||
},
|
||||
|
||||
loadNearestPostToDate(date) {
|
||||
const url = `/posts/by-date/${this.get("topic.id")}/${date}`;
|
||||
const store = this.store;
|
||||
|
||||
return ajax(url).then(post => {
|
||||
return this.storePost(store.createRecord("post", post));
|
||||
});
|
||||
},
|
||||
|
||||
loadPost(postId) {
|
||||
const url = "/posts/" + postId;
|
||||
const store = this.store;
|
||||
|
|
|
@ -1,8 +1,29 @@
|
|||
{{#d-modal-body title="topic.progress.jump_prompt_long"}}
|
||||
{{input id="post-jump" type="number" value=postNumber insert-newline="jump"}}
|
||||
<span class='input-hint-text'>
|
||||
{{i18n "topic.progress.jump_prompt_of" count=topic.postStream.filteredPostsCount}}
|
||||
</span>
|
||||
|
||||
<div class="jumpt-to-post-form">
|
||||
<div class="jump-to-post-control">
|
||||
<span class="index">#</span>
|
||||
{{input id="post-jump" type="number" value=postNumber insert-newline="jump"}}
|
||||
<span class='input-hint-text post-number'>
|
||||
{{i18n "topic.progress.jump_prompt_of" count=filteredPostsCount}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="separator">
|
||||
<hr class="left" />
|
||||
<span class="text">
|
||||
{{i18n "topic.progress.jump_prompt_or"}}
|
||||
</span>
|
||||
<hr class="right" />
|
||||
</div>
|
||||
|
||||
<div class="jump-to-date-control">
|
||||
<span class='input-hint-text post-date'>
|
||||
{{i18n "topic.progress.jump_prompt_to_date"}}
|
||||
</span>
|
||||
{{date-picker id="post-date" class="date-input" value=postDate defaultDate="YYYY-MM-DD"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class='modal-footer'>
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
{{partial "selected-posts"}}
|
||||
</div>
|
||||
|
||||
{{#topic-navigation topic=model jumpToIndex=(action "jumpToIndex") as |info|}}
|
||||
{{#topic-navigation topic=model jumpToDate=(action "jumpToDate") jumpToIndex=(action "jumpToIndex") as |info|}}
|
||||
{{#if info.renderTimeline}}
|
||||
{{#if info.renderAdminMenuButton}}
|
||||
{{topic-admin-menu-button
|
||||
|
|
|
@ -525,6 +525,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
.jump-to-post-modal {
|
||||
.modal-body {
|
||||
#post-jump,
|
||||
.date-picker {
|
||||
margin: 0;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.jump-to-post-control .index {
|
||||
color: $primary-medium;
|
||||
}
|
||||
|
||||
.separator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 1em auto;
|
||||
|
||||
.left,
|
||||
.right {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin: 0 0.5em;
|
||||
color: $primary-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabbed-modal {
|
||||
.modal-body {
|
||||
position: relative;
|
||||
|
|
|
@ -12,6 +12,7 @@ class PostsController < ApplicationController
|
|||
:show,
|
||||
:replies,
|
||||
:by_number,
|
||||
:by_date,
|
||||
:short_link,
|
||||
:reply_history,
|
||||
:replyIids,
|
||||
|
@ -249,6 +250,11 @@ class PostsController < ApplicationController
|
|||
display_post(post)
|
||||
end
|
||||
|
||||
def by_date
|
||||
post = find_post_from_params_by_date
|
||||
display_post(post)
|
||||
end
|
||||
|
||||
def reply_history
|
||||
post = find_post_from_params
|
||||
render_serialized(post.reply_history(params[:max_replies].to_i, guardian), PostSerializer)
|
||||
|
@ -707,6 +713,16 @@ class PostsController < ApplicationController
|
|||
find_post_using(by_number_finder)
|
||||
end
|
||||
|
||||
def find_post_from_params_by_date
|
||||
by_date_finder = TopicView.new(params[:topic_id], current_user)
|
||||
.filtered_posts
|
||||
.where("created_at >= ?", Time.zone.parse(params[:date]))
|
||||
.order("created_at ASC")
|
||||
.limit(1)
|
||||
|
||||
find_post_using(by_date_finder)
|
||||
end
|
||||
|
||||
def find_post_using(finder)
|
||||
# Include deleted posts if the user is staff
|
||||
finder = finder.with_deleted if current_user.try(:staff?)
|
||||
|
|
|
@ -1777,6 +1777,8 @@ en:
|
|||
jump_prompt_of: "of %{count} posts"
|
||||
jump_prompt_long: "What post would you like to jump to?"
|
||||
jump_bottom_with_number: "jump to post %{post_number}"
|
||||
jump_prompt_to_date: "to date"
|
||||
jump_prompt_or: "or"
|
||||
total: total posts
|
||||
current: current post
|
||||
|
||||
|
|
|
@ -457,6 +457,7 @@ Discourse::Application.routes.draw do
|
|||
get "posts" => "posts#latest", id: "latest_posts"
|
||||
get "private-posts" => "posts#latest", id: "private_posts"
|
||||
get "posts/by_number/:topic_id/:post_number" => "posts#by_number"
|
||||
get "posts/by-date/:topic_id/:date" => "posts#by_date"
|
||||
get "posts/:id/reply-history" => "posts#reply_history"
|
||||
get "posts/:id/reply-ids" => "posts#reply_ids"
|
||||
get "posts/:id/reply-ids/all" => "posts#all_reply_ids"
|
||||
|
|
|
@ -92,6 +92,30 @@ describe PostsController do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#by_date' do
|
||||
include_examples 'finding and showing post' do
|
||||
let(:url) { "/posts/by-date/#{post.topic_id}/#{post.created_at.strftime("%Y-%m-%d")}.json" }
|
||||
end
|
||||
|
||||
it 'returns the expected post' do
|
||||
first_post = Fabricate(:post, created_at: 10.days.ago)
|
||||
second_post = Fabricate(:post, topic: first_post.topic, created_at: 4.days.ago)
|
||||
third_post = Fabricate(:post, topic: first_post.topic, created_at: 3.days.ago)
|
||||
|
||||
get "/posts/by-date/#{second_post.topic_id}/#{(second_post.created_at - 2.days).strftime("%Y-%m-%d")}.json"
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json["id"]).to eq(second_post.id)
|
||||
end
|
||||
|
||||
it 'returns no post if date is > at last created post' do
|
||||
get "/posts/by-date/#{post.topic_id}/2245-11-11.json"
|
||||
json = JSON.parse(response.body)
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reply_history' do
|
||||
include_examples 'finding and showing post' do
|
||||
let(:url) { "/posts/#{post.id}/reply-history.json" }
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { acceptance } from "helpers/qunit-helpers";
|
||||
|
||||
acceptance("Jump to", {
|
||||
loggedIn: true,
|
||||
|
||||
pretend(server, helper) {
|
||||
server.get("/t/280/excerpts.json", () => helper.response(200, []));
|
||||
server.get("/t/280/3.json", () => helper.response(200, {}));
|
||||
server.get("/posts/by-date/280/:date", req => {
|
||||
if (req.params["date"] === "2014-02-24") {
|
||||
return helper.response(200, {
|
||||
post_number: 3
|
||||
});
|
||||
}
|
||||
|
||||
return helper.response(404, null);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test("default", async assert => {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
await click("nav#topic-progress .nums");
|
||||
await click("button.jump-to-post");
|
||||
|
||||
assert.ok(exists(".jump-to-post-modal"), "it shows the modal");
|
||||
|
||||
await fillIn("input.date-picker", "2014-02-24");
|
||||
await click(".jump-to-post-modal .btn-primary");
|
||||
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/t/internationalization-localization/280/3",
|
||||
"it jumps to the correct post"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("invalid date", async assert => {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click("nav#topic-progress .nums");
|
||||
await click("button.jump-to-post");
|
||||
await fillIn("input.date-picker", "2094-02-24");
|
||||
await click(".jump-to-post-modal .btn-primary");
|
||||
|
||||
assert.equal(
|
||||
currentURL(),
|
||||
"/t/internationalization-localization/280/20",
|
||||
"it jumps to the last post if no post found"
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue