mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
FEATURE: Add last visit indication to topic view page. (#13471)
This PR also removes grey old unread bubble from the topic badges by dropping `TopicUser#highest_seen_post_number`.
This commit is contained in:
parent
0f688f45bd
commit
37b8ce79c9
|
@ -49,7 +49,9 @@ export default MountWidget.extend({
|
||||||
"selectedPostsCount",
|
"selectedPostsCount",
|
||||||
"searchService",
|
"searchService",
|
||||||
"showReadIndicator",
|
"showReadIndicator",
|
||||||
"streamFilters"
|
"streamFilters",
|
||||||
|
"lastReadPostNumber",
|
||||||
|
"highestPostNumber"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -142,8 +142,8 @@ export default Component.extend({
|
||||||
classes.push("unseen-topic");
|
classes.push("unseen-topic");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topic.get("displayNewPosts")) {
|
if (topic.unread_posts) {
|
||||||
classes.push("new-posts");
|
classes.push("unread-posts");
|
||||||
}
|
}
|
||||||
|
|
||||||
["liked", "archived", "bookmarked", "pinned", "closed"].forEach((name) => {
|
["liked", "archived", "bookmarked", "pinned", "closed"].forEach((name) => {
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { or } from "@ember/object/computed";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "span",
|
tagName: "span",
|
||||||
classNameBindings: [":topic-post-badges"],
|
classNameBindings: [":topic-post-badges"],
|
||||||
rerenderTriggers: ["url", "unread", "newPosts", "unseen"],
|
rerenderTriggers: ["url", "unread", "newPosts", "unreadPosts", "unseen"],
|
||||||
newDotText: null,
|
newDotText: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
this.set(
|
this.set(
|
||||||
"newDotText",
|
"newDotText",
|
||||||
this.currentUser && this.currentUser.trust_level > 0
|
this.currentUser && this.currentUser.trust_level > 0
|
||||||
|
@ -15,4 +18,6 @@ export default Component.extend({
|
||||||
: I18n.t("filters.new.lower_title")
|
: I18n.t("filters.new.lower_title")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
displayUnreadPosts: or("newPosts", "unreadPosts"),
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,6 +68,8 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||||
filter: null,
|
filter: null,
|
||||||
quoteState: null,
|
quoteState: null,
|
||||||
currentPostId: null,
|
currentPostId: null,
|
||||||
|
userLastReadPostNumber: null,
|
||||||
|
highestPostNumber: null,
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
|
@ -352,15 +352,14 @@ const TopicTrackingState = EmberObject.extend({
|
||||||
isSeen !== state.is_seen
|
isSeen !== state.is_seen
|
||||||
) {
|
) {
|
||||||
const postsCount = topic.get("posts_count");
|
const postsCount = topic.get("posts_count");
|
||||||
let newPosts = postsCount - state.highest_post_number,
|
let unread;
|
||||||
unread = postsCount - state.last_read_post_number;
|
|
||||||
|
|
||||||
if (newPosts < 0) {
|
if (state.last_read_post_number) {
|
||||||
newPosts = 0;
|
unread = postsCount - state.last_read_post_number;
|
||||||
}
|
} else {
|
||||||
if (!state.last_read_post_number) {
|
|
||||||
unread = 0;
|
unread = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unread < 0) {
|
if (unread < 0) {
|
||||||
unread = 0;
|
unread = 0;
|
||||||
}
|
}
|
||||||
|
@ -368,8 +367,7 @@ const TopicTrackingState = EmberObject.extend({
|
||||||
topic.setProperties({
|
topic.setProperties({
|
||||||
highest_post_number: state.highest_post_number,
|
highest_post_number: state.highest_post_number,
|
||||||
last_read_post_number: state.last_read_post_number,
|
last_read_post_number: state.last_read_post_number,
|
||||||
new_posts: newPosts,
|
unread_posts: unread,
|
||||||
unread: unread,
|
|
||||||
is_seen: state.is_seen,
|
is_seen: state.is_seen,
|
||||||
unseen: !state.last_read_post_number && isUnseen(state),
|
unseen: !state.last_read_post_number && isUnseen(state),
|
||||||
});
|
});
|
||||||
|
@ -654,14 +652,13 @@ const TopicTrackingState = EmberObject.extend({
|
||||||
newState.topic_id = topic.id;
|
newState.topic_id = topic.id;
|
||||||
newState.notification_level = topic.notification_level;
|
newState.notification_level = topic.notification_level;
|
||||||
|
|
||||||
// see ListableTopicSerializer for unread/unseen/new_posts and other
|
// see ListableTopicSerializer for unread_posts/unseen and other
|
||||||
// topic property logic
|
// topic property logic
|
||||||
if (topic.unseen) {
|
if (topic.unseen) {
|
||||||
newState.last_read_post_number = null;
|
newState.last_read_post_number = null;
|
||||||
} else if (topic.unread || topic.new_posts) {
|
} else if (topic.unread_posts) {
|
||||||
newState.last_read_post_number =
|
newState.last_read_post_number =
|
||||||
topic.highest_post_number -
|
topic.highest_post_number - (topic.unread_posts || 0);
|
||||||
((topic.unread || 0) + (topic.new_posts || 0));
|
|
||||||
} else {
|
} else {
|
||||||
// remove the topic if it is no longer unread/new (it has been seen)
|
// remove the topic if it is no longer unread/new (it has been seen)
|
||||||
// and if there are too many topics in memory
|
// and if there are too many topics in memory
|
||||||
|
|
|
@ -7,7 +7,6 @@ import I18n from "I18n";
|
||||||
import PreloadStore from "discourse/lib/preload-store";
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
import { Promise } from "rsvp";
|
import { Promise } from "rsvp";
|
||||||
import RestModel from "discourse/models/rest";
|
import RestModel from "discourse/models/rest";
|
||||||
import Session from "discourse/models/session";
|
|
||||||
import Site from "discourse/models/site";
|
import Site from "discourse/models/site";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
@ -21,6 +20,7 @@ import { longDate } from "discourse/lib/formatter";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { resolveShareUrl } from "discourse/helpers/share-url";
|
import { resolveShareUrl } from "discourse/helpers/share-url";
|
||||||
import DiscourseURL, { userPath } from "discourse/lib/url";
|
import DiscourseURL, { userPath } from "discourse/lib/url";
|
||||||
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
|
|
||||||
export function loadTopicView(topic, args) {
|
export function loadTopicView(topic, args) {
|
||||||
const data = deepMerge({}, args);
|
const data = deepMerge({}, args);
|
||||||
|
@ -239,10 +239,16 @@ const Topic = RestModel.extend({
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("new_posts", "unread")
|
@discourseComputed("unread_posts", "new_posts")
|
||||||
totalUnread(newPosts, unread) {
|
totalUnread(unreadPosts, newPosts) {
|
||||||
const count = (unread || 0) + (newPosts || 0);
|
deprecated("The totalUnread property of the topic model is deprecated");
|
||||||
return count > 0 ? count : null;
|
return unreadPosts || newPosts;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("unread_posts", "new_posts")
|
||||||
|
displayNewPosts(unreadPosts, newPosts) {
|
||||||
|
deprecated("The displayNewPosts property of the topic model is deprecated");
|
||||||
|
return unreadPosts || newPosts;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("last_read_post_number", "url")
|
@discourseComputed("last_read_post_number", "url")
|
||||||
|
@ -284,25 +290,6 @@ const Topic = RestModel.extend({
|
||||||
return userPath(username);
|
return userPath(username);
|
||||||
},
|
},
|
||||||
|
|
||||||
// The amount of new posts to display. It might be different than what the server
|
|
||||||
// tells us if we are still asynchronously flushing our "recently read" data.
|
|
||||||
// So take what the browser has seen into consideration.
|
|
||||||
@discourseComputed("new_posts", "id")
|
|
||||||
displayNewPosts(newPosts, id) {
|
|
||||||
const highestSeen = Session.currentProp("highestSeenByTopic")[id];
|
|
||||||
if (highestSeen) {
|
|
||||||
const delta = highestSeen - this.last_read_post_number;
|
|
||||||
if (delta > 0) {
|
|
||||||
let result = newPosts - delta;
|
|
||||||
if (result < 0) {
|
|
||||||
result = 0;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newPosts;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("views")
|
@discourseComputed("views")
|
||||||
viewsHeat(v) {
|
viewsHeat(v) {
|
||||||
if (v >= this.siteSettings.topic_views_heat_high) {
|
if (v >= this.siteSettings.topic_views_heat_high) {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { and, or } from "@ember/object/computed";
|
import { and } from "@ember/object/computed";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
export default EmberObject.extend({
|
export default EmberObject.extend({
|
||||||
postCountsPresent: or("topic.unread", "topic.displayNewPosts"),
|
showBadges: and("postBadgesEnabled", "topic.unread_posts"),
|
||||||
showBadges: and("postBadgesEnabled", "postCountsPresent"),
|
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
newDotText() {
|
newDotText() {
|
||||||
|
|
|
@ -69,6 +69,8 @@ export default DiscourseRoute.extend({
|
||||||
"model.currentPost": closest,
|
"model.currentPost": closest,
|
||||||
enteredIndex: topic.postStream.progressIndexOfPost(closestPost),
|
enteredIndex: topic.postStream.progressIndexOfPost(closestPost),
|
||||||
enteredAt: Date.now().toString(),
|
enteredAt: Date.now().toString(),
|
||||||
|
userLastReadPostNumber: topic.last_read_post_number,
|
||||||
|
highestPostNumber: topic.highest_post_number,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.appEvents.trigger("page:topic-loaded", topic);
|
this.appEvents.trigger("page:topic-loaded", topic);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{raw "topic-status" topic=topic}}
|
{{raw "topic-status" topic=topic}}
|
||||||
<a href={{topic.lastUnreadUrl}} class="title">{{html-safe topic.fancyTitle}}</a>
|
<a href={{topic.lastUnreadUrl}} class="title">{{html-safe topic.fancyTitle}}</a>
|
||||||
{{topic-post-badges newPosts=topic.totalUnread unseen=topic.unseen url=topic.lastUnreadUrl}}
|
{{topic-post-badges unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl}}
|
||||||
|
|
||||||
<a href={{topic.lastPostUrl}} class="last-posted-at">{{format-age topic.last_posted_at}}</a>
|
<a href={{topic.lastPostUrl}} class="last-posted-at">{{format-age topic.last_posted_at}}</a>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{{#if topic.featured_link}}
|
{{#if topic.featured_link}}
|
||||||
{{topic-featured-link topic}}
|
{{topic-featured-link topic}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{topic-post-badges newPosts=topic.totalUnread unseen=topic.unseen url=topic.lastUnreadUrl}}
|
{{topic-post-badges unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl}}
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-row">
|
<div class="bottom-row">
|
||||||
{{category-link topic.category}}{{discourse-tags topic mode="list"}}{{! intentionally inline to avoid whitespace}}
|
{{category-link topic.category}}{{discourse-tags topic mode="list"}}{{! intentionally inline to avoid whitespace}}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
{{#if unread }}
|
{{#if displayUnreadPosts}}
|
||||||
<a href={{url}} title={{i18n "topic.unread_posts" count=unread}} class="badge badge-notification unread">{{unread}}</a>
|
<a href={{url}} title={{i18n "topic.unread_posts" count=displayUnreadPosts}} class="badge badge-notification unread-posts">{{displayUnreadPosts}}</a>
|
||||||
{{/if}}
|
|
||||||
{{#if newPosts}}
|
|
||||||
<a href={{url}} title={{i18n "topic.total_unread_posts" count=newPosts}} class="badge badge-notification new-posts">{{newPosts}}</a>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if unseen}}
|
{{#if unseen}}
|
||||||
<a href={{url}} title={{i18n "topic.new"}} class="badge badge-notification new-topic">{{newDotText}}</a>
|
<a href={{url}} title={{i18n "topic.new"}} class="badge badge-notification new-topic">{{newDotText}}</a>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{#if view.showBadges}}
|
{{#if view.showBadges}}
|
||||||
{{raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}}
|
{{raw "topic-post-badges" unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{raw "list/posts-count-column" topic=topic tagName="div"}}
|
{{raw "list/posts-count-column" topic=topic tagName="div"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
topicId=topic.id
|
topicId=topic.id
|
||||||
unreadClass=unreadClass~}}
|
unreadClass=unreadClass~}}
|
||||||
{{~#if showTopicPostBadges}}
|
{{~#if showTopicPostBadges}}
|
||||||
{{~raw "topic-post-badges" unread=topic.unread newPosts=topic.displayNewPosts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}}
|
{{~raw "topic-post-badges" unreadPosts=topic.unread_posts unseen=topic.unseen url=topic.lastUnreadUrl newDotText=newDotText}}
|
||||||
{{~/if}}
|
{{~/if}}
|
||||||
</span>
|
</span>
|
||||||
<div class="link-bottom-line">
|
<div class="link-bottom-line">
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<span class='topic-post-badges'>
|
<span class='topic-post-badges'>
|
||||||
{{~#if unread ~}}
|
|
||||||
<a href='{{url}}' class='badge badge-notification unread' title='{{i18n "topic.unread_posts" count=unread}}'>{{unread}}</a>
|
|
||||||
{{~/if}}
|
|
||||||
{{~#if newPosts ~}}
|
{{~#if newPosts ~}}
|
||||||
<a href='{{url}}' class='badge badge-notification new-posts' title='{{i18n "topic.total_unread_posts" count=newPosts}}'>{{newPosts}}</a>
|
<a href='{{url}}' class='badge badge-notification unread-posts' title='{{i18n "topic.unread_posts" count=newPosts}}'>{{newPosts}}</a>
|
||||||
|
{{~/if}}
|
||||||
|
{{~#if unreadPosts ~}}
|
||||||
|
<a href='{{url}}' class='badge badge-notification unread-posts' title='{{i18n "topic.unread_posts" count=unreadPosts}}'>{{unreadPosts}}</a>
|
||||||
{{~/if}}
|
{{~/if}}
|
||||||
{{~#if unseen ~}}
|
{{~#if unseen ~}}
|
||||||
<a href='{{url}}' class='badge badge-notification new-topic' title='{{i18n "topic.new"}}'>{{newDotText}}</a>
|
<a href='{{url}}' class='badge badge-notification new-topic' title='{{i18n "topic.new"}}'>{{newDotText}}</a>
|
||||||
|
|
|
@ -208,6 +208,8 @@
|
||||||
gaps=model.postStream.gaps
|
gaps=model.postStream.gaps
|
||||||
showReadIndicator=model.show_read_indicator
|
showReadIndicator=model.show_read_indicator
|
||||||
streamFilters=model.postStream.streamFilters
|
streamFilters=model.postStream.streamFilters
|
||||||
|
lastReadPostNumber=userLastReadPostNumber
|
||||||
|
highestPostNumber=highestPostNumber
|
||||||
showFlags=(action "showPostFlags")
|
showFlags=(action "showPostFlags")
|
||||||
editPost=(action "editPost")
|
editPost=(action "editPost")
|
||||||
showHistory=(route-action "showHistory")
|
showHistory=(route-action "showHistory")
|
||||||
|
|
|
@ -186,17 +186,19 @@ export default createWidget("post-stream", {
|
||||||
tagName: "div.post-stream",
|
tagName: "div.post-stream",
|
||||||
|
|
||||||
html(attrs) {
|
html(attrs) {
|
||||||
const posts = attrs.posts || [],
|
const posts = attrs.posts || [];
|
||||||
postArray = posts.toArray(),
|
const postArray = posts.toArray();
|
||||||
result = [],
|
const postArrayLength = postArray.length;
|
||||||
before = attrs.gaps && attrs.gaps.before ? attrs.gaps.before : {},
|
const maxPostNumber = postArray[postArrayLength - 1].post_number;
|
||||||
after = attrs.gaps && attrs.gaps.after ? attrs.gaps.after : {},
|
const result = [];
|
||||||
mobileView = this.site.mobileView;
|
const before = attrs.gaps && attrs.gaps.before ? attrs.gaps.before : {};
|
||||||
|
const after = attrs.gaps && attrs.gaps.after ? attrs.gaps.after : {};
|
||||||
|
const mobileView = this.site.mobileView;
|
||||||
|
|
||||||
let prevPost;
|
let prevPost;
|
||||||
let prevDate;
|
let prevDate;
|
||||||
|
|
||||||
for (let i = 0; i < postArray.length; i++) {
|
for (let i = 0; i < postArrayLength; i++) {
|
||||||
const post = postArray[i];
|
const post = postArray[i];
|
||||||
|
|
||||||
if (post instanceof Placeholder) {
|
if (post instanceof Placeholder) {
|
||||||
|
@ -276,6 +278,18 @@ export default createWidget("post-stream", {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
i !== postArrayLength - 1 &&
|
||||||
|
maxPostNumber <= attrs.highestPostNumber &&
|
||||||
|
attrs.lastReadPostNumber === post.post_number
|
||||||
|
) {
|
||||||
|
result.push(
|
||||||
|
this.attach("topic-post-visited-line", {
|
||||||
|
post_number: post.post_number,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
prevPost = post;
|
prevPost = post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { createWidget } from "discourse/widgets/widget";
|
||||||
|
import { h } from "virtual-dom";
|
||||||
|
|
||||||
|
export default createWidget("topic-post-visited-line", {
|
||||||
|
tagName: "div.topic-post-visited-line",
|
||||||
|
|
||||||
|
buildClasses(attrs) {
|
||||||
|
return [`post-${attrs.post_number}`];
|
||||||
|
},
|
||||||
|
|
||||||
|
html() {
|
||||||
|
return h(
|
||||||
|
"span.topic-post-visited-message",
|
||||||
|
I18n.t("topics.new_messages_marker")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -53,8 +53,7 @@ acceptance("Tags", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 1,
|
||||||
new_posts: 1,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -553,3 +553,23 @@ acceptance("Topic pinning/unpinning as a group moderator", function (needs) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
acceptance("Topic last visit line", function (needs) {
|
||||||
|
needs.user({ moderator: false, admin: false, trust_level: 1 });
|
||||||
|
|
||||||
|
test("visit topic", async function (assert) {
|
||||||
|
await visit("/t/-/280");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists(".topic-post-visited-line.post-10"),
|
||||||
|
"shows the last visited line on the right post"
|
||||||
|
);
|
||||||
|
|
||||||
|
await visit("/t/-/9");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
!exists(".topic-post-visited-line"),
|
||||||
|
"does not show last visited line if post is the last post"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -4033,8 +4033,7 @@ export default {
|
||||||
bumped_at: "2019-11-12T05:19:52.848Z",
|
bumped_at: "2019-11-12T05:19:52.848Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4076,8 +4075,7 @@ export default {
|
||||||
bumped_at: "2019-11-12T05:19:32.516Z",
|
bumped_at: "2019-11-12T05:19:32.516Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -6398,8 +6396,7 @@ export default {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -38,8 +38,7 @@ export default {
|
||||||
bumped_at: "2019-07-26T01:29:24.177Z",
|
bumped_at: "2019-07-26T01:29:24.177Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -3018,8 +3018,7 @@ export default {
|
||||||
bumped_at: "2015-04-08T16:05:09.842Z",
|
bumped_at: "2015-04-08T16:05:09.842Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
unread: 0,
|
unread_posts: 1,
|
||||||
new_posts: 1,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -3062,8 +3061,7 @@ export default {
|
||||||
bumped_at: "2015-04-08T15:40:30.037Z",
|
bumped_at: "2015-04-08T15:40:30.037Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
unread: 0,
|
unread_posts: 2,
|
||||||
new_posts: 2,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -3143,8 +3141,7 @@ export default {
|
||||||
bumped_at: "2015-04-07T09:21:21.570Z",
|
bumped_at: "2015-04-07T09:21:21.570Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 8,
|
last_read_post_number: 8,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -3187,8 +3184,7 @@ export default {
|
||||||
bumped_at: "2015-02-22T13:46:26.845Z",
|
bumped_at: "2015-02-22T13:46:26.845Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 65,
|
last_read_post_number: 65,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -3266,8 +3262,7 @@ export default {
|
||||||
bumped_at: "2015-03-21T00:33:52.243Z",
|
bumped_at: "2015-03-21T00:33:52.243Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4087,8 +4082,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T10:14:34.799Z",
|
bumped_at: "2015-08-13T10:14:34.799Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4117,8 +4111,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T01:58:35.206Z",
|
bumped_at: "2015-08-13T01:58:35.206Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4175,8 +4168,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T10:14:34.799Z",
|
bumped_at: "2015-08-13T10:14:34.799Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4413,8 +4405,7 @@ export default {
|
||||||
bumped_at: "2017-01-27T03:52:02.119Z",
|
bumped_at: "2017-01-27T03:52:02.119Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4690,8 +4681,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T10:14:34.799Z",
|
bumped_at: "2015-08-13T10:14:34.799Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4720,8 +4710,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T01:58:35.206Z",
|
bumped_at: "2015-08-13T01:58:35.206Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -4982,8 +4971,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T10:14:34.799Z",
|
bumped_at: "2015-08-13T10:14:34.799Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -5012,8 +5000,7 @@ export default {
|
||||||
bumped_at: "2015-08-13T01:58:35.206Z",
|
bumped_at: "2015-08-13T01:58:35.206Z",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -247,8 +247,7 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
|
||||||
{
|
{
|
||||||
highest_post_number: null,
|
highest_post_number: null,
|
||||||
id: 111,
|
id: 111,
|
||||||
unread: 10,
|
unread_posts: 10,
|
||||||
new_posts: 10,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -271,8 +270,7 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
|
||||||
Topic.create({
|
Topic.create({
|
||||||
highest_post_number: null,
|
highest_post_number: null,
|
||||||
id: 111,
|
id: 111,
|
||||||
unread: 10,
|
unread_posts: 10,
|
||||||
new_posts: 10,
|
|
||||||
unseen: true,
|
unseen: true,
|
||||||
prevent_sync: false,
|
prevent_sync: false,
|
||||||
}),
|
}),
|
||||||
|
@ -303,8 +301,7 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
|
||||||
id: 111,
|
id: 111,
|
||||||
unseen: false,
|
unseen: false,
|
||||||
seen: true,
|
seen: true,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
prevent_sync: false,
|
prevent_sync: false,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -338,8 +335,7 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
|
||||||
id: 111,
|
id: 111,
|
||||||
unseen: true,
|
unseen: true,
|
||||||
seen: false,
|
seen: false,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
highest_post_number: 20,
|
highest_post_number: 20,
|
||||||
category_id: 1,
|
category_id: 1,
|
||||||
tags: ["pending"],
|
tags: ["pending"],
|
||||||
|
@ -348,8 +344,7 @@ discourseModule("Unit | Model | topic-tracking-state", function (hooks) {
|
||||||
id: 222,
|
id: 222,
|
||||||
unseen: false,
|
unseen: false,
|
||||||
seen: true,
|
seen: true,
|
||||||
unread: 3,
|
unread_posts: 3,
|
||||||
new_posts: 0,
|
|
||||||
highest_post_number: 20,
|
highest_post_number: 20,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
@ -396,7 +396,7 @@ div.education {
|
||||||
border-top: 3px solid var(--primary-low);
|
border-top: 3px solid var(--primary-low);
|
||||||
border-bottom: 1px solid var(--primary-low);
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
|
||||||
.badge-notification.new-posts {
|
.badge-notification.unread-posts {
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1030,6 +1030,21 @@ a.mention-group {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-post-visited-line {
|
||||||
|
border-bottom: 1px solid var(--danger-medium);
|
||||||
|
line-height: 0.1em;
|
||||||
|
text-align: center;
|
||||||
|
margin: 1rem 0px;
|
||||||
|
|
||||||
|
.topic-post-visited-message {
|
||||||
|
position: relative; // Chrome needs this, otherwise the line is above the text
|
||||||
|
background-color: var(--secondary);
|
||||||
|
color: var(--danger-medium);
|
||||||
|
font-size: $font-down-1;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Select posts
|
// Select posts
|
||||||
|
|
||||||
.topic-post {
|
.topic-post {
|
||||||
|
|
|
@ -178,9 +178,8 @@
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
// New posts
|
// Unread posts
|
||||||
|
|
||||||
&.new-posts,
|
|
||||||
&.unread-posts {
|
&.unread-posts {
|
||||||
background-color: var(--tertiary-med-or-tertiary);
|
background-color: var(--tertiary-med-or-tertiary);
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
.badge-wrapper {
|
.badge-wrapper {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
.badge-notification.new-posts {
|
.badge-notification.unread-posts {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
.topic-statuses {
|
.topic-statuses {
|
||||||
margin-right: 0.15em;
|
margin-right: 0.15em;
|
||||||
}
|
}
|
||||||
.topic-post-badges .badge.new-posts,
|
.topic-post-badges .badge.unread-posts,
|
||||||
.title {
|
.title {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -577,6 +577,20 @@ blockquote {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-post-visited-line {
|
||||||
|
width: calc(
|
||||||
|
#{$topic-body-width} + #{$topic-avatar-width} +
|
||||||
|
(#{$topic-body-width-padding} * 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
+ .topic-post {
|
||||||
|
.topic-avatar,
|
||||||
|
.topic-body {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// variables are used to calculate the width of .gap
|
// variables are used to calculate the width of .gap
|
||||||
.topic-body {
|
.topic-body {
|
||||||
width: calc(#{$topic-body-width} + (#{$topic-body-width-padding} * 2));
|
width: calc(#{$topic-body-width} + (#{$topic-body-width-padding} * 2));
|
||||||
|
|
|
@ -156,7 +156,6 @@ ol.category-breadcrumb {
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-topic-link td.num .badge-notification {
|
.category-topic-link td.num .badge-notification {
|
||||||
&.new-posts,
|
|
||||||
&.unread-posts {
|
&.unread-posts {
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-post-visited-line {
|
||||||
|
+ .topic-post article {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.topic-post article {
|
.topic-post article {
|
||||||
border-top: 1px solid var(--primary-low);
|
border-top: 1px solid var(--primary-low);
|
||||||
padding: 15px 0 8px 0;
|
padding: 15px 0 8px 0;
|
||||||
|
|
|
@ -136,7 +136,6 @@ html {
|
||||||
background: var(--primary-medium);
|
background: var(--primary-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-notification.new-posts,
|
|
||||||
.badge-notification.unread-posts {
|
.badge-notification.unread-posts {
|
||||||
background: var(--tertiary);
|
background: var(--tertiary);
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,7 +352,7 @@ class PostMover
|
||||||
}
|
}
|
||||||
|
|
||||||
DB.exec(<<~SQL, params)
|
DB.exec(<<~SQL, params)
|
||||||
INSERT INTO topic_users(user_id, topic_id, posted, last_read_post_number, highest_seen_post_number,
|
INSERT INTO topic_users(user_id, topic_id, posted, last_read_post_number,
|
||||||
last_emailed_post_number, first_visited_at, last_visited_at, notification_level,
|
last_emailed_post_number, first_visited_at, last_visited_at, notification_level,
|
||||||
notifications_changed_at, notifications_reason_id)
|
notifications_changed_at, notifications_reason_id)
|
||||||
SELECT tu.user_id,
|
SELECT tu.user_id,
|
||||||
|
@ -370,12 +370,6 @@ class PostMover
|
||||||
WHERE lr.old_topic_id = tu.topic_id
|
WHERE lr.old_topic_id = tu.topic_id
|
||||||
AND lr.old_post_number <= tu.last_read_post_number
|
AND lr.old_post_number <= tu.last_read_post_number
|
||||||
) AS last_read_post_number,
|
) AS last_read_post_number,
|
||||||
(
|
|
||||||
SELECT MAX(hs.new_post_number)
|
|
||||||
FROM moved_posts hs
|
|
||||||
WHERE hs.old_topic_id = tu.topic_id
|
|
||||||
AND hs.old_post_number <= tu.highest_seen_post_number
|
|
||||||
) AS highest_seen_post_number,
|
|
||||||
(
|
(
|
||||||
SELECT MAX(le.new_post_number)
|
SELECT MAX(le.new_post_number)
|
||||||
FROM moved_posts le
|
FROM moved_posts le
|
||||||
|
@ -392,7 +386,6 @@ class PostMover
|
||||||
WHERE tu.topic_id = :old_topic_id
|
WHERE tu.topic_id = :old_topic_id
|
||||||
AND GREATEST(
|
AND GREATEST(
|
||||||
tu.last_read_post_number,
|
tu.last_read_post_number,
|
||||||
tu.highest_seen_post_number,
|
|
||||||
tu.last_emailed_post_number
|
tu.last_emailed_post_number
|
||||||
) >= (SELECT MIN(old_post_number) FROM moved_posts)
|
) >= (SELECT MIN(old_post_number) FROM moved_posts)
|
||||||
ON CONFLICT (topic_id, user_id) DO UPDATE
|
ON CONFLICT (topic_id, user_id) DO UPDATE
|
||||||
|
@ -409,18 +402,6 @@ class PostMover
|
||||||
GREATEST(topic_users.last_read_post_number,
|
GREATEST(topic_users.last_read_post_number,
|
||||||
excluded.last_read_post_number)
|
excluded.last_read_post_number)
|
||||||
ELSE topic_users.last_read_post_number END,
|
ELSE topic_users.last_read_post_number END,
|
||||||
highest_seen_post_number = CASE
|
|
||||||
WHEN topic_users.highest_seen_post_number = :old_highest_staff_post_number OR (
|
|
||||||
:old_highest_post_number < :old_highest_staff_post_number
|
|
||||||
AND topic_users.highest_seen_post_number = :old_highest_post_number
|
|
||||||
AND NOT EXISTS(SELECT 1
|
|
||||||
FROM users u
|
|
||||||
WHERE u.id = topic_users.user_id
|
|
||||||
AND (admin OR moderator))
|
|
||||||
) THEN
|
|
||||||
GREATEST(topic_users.highest_seen_post_number,
|
|
||||||
excluded.highest_seen_post_number)
|
|
||||||
ELSE topic_users.highest_seen_post_number END,
|
|
||||||
last_emailed_post_number = CASE
|
last_emailed_post_number = CASE
|
||||||
WHEN topic_users.last_emailed_post_number = :old_highest_staff_post_number OR (
|
WHEN topic_users.last_emailed_post_number = :old_highest_staff_post_number OR (
|
||||||
:old_highest_post_number < :old_highest_staff_post_number
|
:old_highest_post_number < :old_highest_staff_post_number
|
||||||
|
|
|
@ -71,7 +71,6 @@ class PostTiming < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
TopicUser.where(user_id: user.id, topic_id: topic.id).update_all(
|
TopicUser.where(user_id: user.id, topic_id: topic.id).update_all(
|
||||||
highest_seen_post_number: last_read,
|
|
||||||
last_read_post_number: last_read
|
last_read_post_number: last_read
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -810,11 +810,7 @@ class Topic < ActiveRecord::Base
|
||||||
SET last_read_post_number = CASE
|
SET last_read_post_number = CASE
|
||||||
WHEN last_read_post_number > :highest THEN :highest
|
WHEN last_read_post_number > :highest THEN :highest
|
||||||
ELSE last_read_post_number
|
ELSE last_read_post_number
|
||||||
END,
|
END
|
||||||
highest_seen_post_number = CASE
|
|
||||||
WHEN highest_seen_post_number > :highest THEN :highest
|
|
||||||
ELSE highest_seen_post_number
|
|
||||||
END
|
|
||||||
WHERE topic_id = :topic_id
|
WHERE topic_id = :topic_id
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class TopicUser < ActiveRecord::Base
|
class TopicUser < ActiveRecord::Base
|
||||||
|
self.ignored_columns = [
|
||||||
|
:highest_seen_post_number # Remove after 01 Jan 2022
|
||||||
|
]
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
|
|
||||||
|
@ -114,6 +118,11 @@ class TopicUser < ActiveRecord::Base
|
||||||
# since there's more likely to be an existing record than not. If the update returns 0 rows affected
|
# since there's more likely to be an existing record than not. If the update returns 0 rows affected
|
||||||
# it then creates the row instead.
|
# it then creates the row instead.
|
||||||
def change(user_id, topic_id, attrs)
|
def change(user_id, topic_id, attrs)
|
||||||
|
# For plugin compatibility, remove after 01 Jan 2022
|
||||||
|
if attrs[:highest_seen_post_number]
|
||||||
|
attrs.delete(:highest_seen_post_number)
|
||||||
|
end
|
||||||
|
|
||||||
# Sometimes people pass objs instead of the ids. We can handle that.
|
# Sometimes people pass objs instead of the ids. We can handle that.
|
||||||
topic_id = topic_id.id if topic_id.is_a?(::Topic)
|
topic_id = topic_id.id if topic_id.is_a?(::Topic)
|
||||||
user_id = user_id.id if user_id.is_a?(::User)
|
user_id = user_id.id if user_id.is_a?(::User)
|
||||||
|
@ -131,6 +140,7 @@ class TopicUser < ActiveRecord::Base
|
||||||
|
|
||||||
attrs_sql = attrs_array.map { |t| "#{t[0]} = ?" }.join(", ")
|
attrs_sql = attrs_array.map { |t| "#{t[0]} = ?" }.join(", ")
|
||||||
vals = attrs_array.map { |t| t[1] }
|
vals = attrs_array.map { |t| t[1] }
|
||||||
|
|
||||||
rows = TopicUser.where(topic_id: topic_id, user_id: user_id).update_all([attrs_sql, *vals])
|
rows = TopicUser.where(topic_id: topic_id, user_id: user_id).update_all([attrs_sql, *vals])
|
||||||
|
|
||||||
if rows == 0
|
if rows == 0
|
||||||
|
@ -252,7 +262,6 @@ class TopicUser < ActiveRecord::Base
|
||||||
UPDATE_TOPIC_USER_SQL = "UPDATE topic_users
|
UPDATE_TOPIC_USER_SQL = "UPDATE topic_users
|
||||||
SET
|
SET
|
||||||
last_read_post_number = GREATEST(:post_number, tu.last_read_post_number),
|
last_read_post_number = GREATEST(:post_number, tu.last_read_post_number),
|
||||||
highest_seen_post_number = t.highest_post_number,
|
|
||||||
total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000),
|
total_msecs_viewed = LEAST(tu.total_msecs_viewed + :msecs,86400000),
|
||||||
notification_level =
|
notification_level =
|
||||||
case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
|
case when tu.notifications_reason_id is null and (tu.total_msecs_viewed + :msecs) >
|
||||||
|
@ -278,8 +287,8 @@ class TopicUser < ActiveRecord::Base
|
||||||
|
|
||||||
UPDATE_TOPIC_USER_SQL_STAFF = UPDATE_TOPIC_USER_SQL.gsub("highest_post_number", "highest_staff_post_number")
|
UPDATE_TOPIC_USER_SQL_STAFF = UPDATE_TOPIC_USER_SQL.gsub("highest_post_number", "highest_staff_post_number")
|
||||||
|
|
||||||
INSERT_TOPIC_USER_SQL = "INSERT INTO topic_users (user_id, topic_id, last_read_post_number, highest_seen_post_number, last_visited_at, first_visited_at, notification_level)
|
INSERT_TOPIC_USER_SQL = "INSERT INTO topic_users (user_id, topic_id, last_read_post_number, last_visited_at, first_visited_at, notification_level)
|
||||||
SELECT :user_id, :topic_id, :post_number, ft.highest_post_number, :now, :now, :new_status
|
SELECT :user_id, :topic_id, :post_number, :now, :now, :new_status
|
||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
JOIN users u on u.id = :user_id
|
JOIN users u on u.id = :user_id
|
||||||
WHERE ft.id = :topic_id
|
WHERE ft.id = :topic_id
|
||||||
|
@ -303,11 +312,6 @@ class TopicUser < ActiveRecord::Base
|
||||||
threshold: SiteSetting.default_other_auto_track_topics_after_msecs
|
threshold: SiteSetting.default_other_auto_track_topics_after_msecs
|
||||||
}
|
}
|
||||||
|
|
||||||
# In case anyone sees "highest_seen_post_number" and gets confused, like I do.
|
|
||||||
# highest_seen_post_number represents the highest_post_number of the topic when
|
|
||||||
# the user visited it. It may be out of alignment with last_read, meaning
|
|
||||||
# ... user visited the topic but did not read the posts
|
|
||||||
#
|
|
||||||
# 86400000 = 1 day
|
# 86400000 = 1 day
|
||||||
rows =
|
rows =
|
||||||
if user.staff?
|
if user.staff?
|
||||||
|
@ -424,12 +428,11 @@ class TopicUser < ActiveRecord::Base
|
||||||
builder.exec(action_type_id: PostActionType.types[action_type])
|
builder.exec(action_type_id: PostActionType.types[action_type])
|
||||||
end
|
end
|
||||||
|
|
||||||
# cap number of unread topics at count, bumping up highest_seen / last_read if needed
|
# cap number of unread topics at count, bumping up last_read if needed
|
||||||
def self.cap_unread!(user_id, count)
|
def self.cap_unread!(user_id, count)
|
||||||
sql = <<SQL
|
sql = <<SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET last_read_post_number = max_number,
|
SET last_read_post_number = max_number
|
||||||
highest_seen_post_number = max_number
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT MAX(post_number) max_number, p.topic_id FROM posts p
|
SELECT MAX(post_number) max_number, p.topic_id FROM posts p
|
||||||
WHERE deleted_at IS NULL
|
WHERE deleted_at IS NULL
|
||||||
|
@ -456,8 +459,7 @@ SQL
|
||||||
builder = DB.build <<~SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE topic_users t
|
UPDATE topic_users t
|
||||||
SET
|
SET
|
||||||
last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number),
|
last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number)
|
||||||
highest_seen_post_number = LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT topic_id, user_id, MAX(post_number) last_read
|
SELECT topic_id, user_id, MAX(post_number) last_read
|
||||||
FROM post_timings
|
FROM post_timings
|
||||||
|
@ -474,8 +476,7 @@ SQL
|
||||||
X.topic_id = t.topic_id AND
|
X.topic_id = t.topic_id AND
|
||||||
X.user_id = t.user_id AND
|
X.user_id = t.user_id AND
|
||||||
(
|
(
|
||||||
last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number) OR
|
last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number)
|
||||||
highest_seen_post_number <> LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
|
@ -496,7 +497,6 @@ end
|
||||||
# topic_id :integer not null
|
# topic_id :integer not null
|
||||||
# posted :boolean default(FALSE), not null
|
# posted :boolean default(FALSE), not null
|
||||||
# last_read_post_number :integer
|
# last_read_post_number :integer
|
||||||
# highest_seen_post_number :integer
|
|
||||||
# last_visited_at :datetime
|
# last_visited_at :datetime
|
||||||
# first_visited_at :datetime
|
# first_visited_at :datetime
|
||||||
# notification_level :integer default(1), not null
|
# notification_level :integer default(1), not null
|
||||||
|
|
|
@ -14,6 +14,7 @@ class ListableTopicSerializer < BasicTopicSerializer
|
||||||
:last_read_post_number,
|
:last_read_post_number,
|
||||||
:unread,
|
:unread,
|
||||||
:new_posts,
|
:new_posts,
|
||||||
|
:unread_posts,
|
||||||
:pinned,
|
:pinned,
|
||||||
:unpinned,
|
:unpinned,
|
||||||
:excerpt,
|
:excerpt,
|
||||||
|
@ -115,16 +116,25 @@ class ListableTopicSerializer < BasicTopicSerializer
|
||||||
|
|
||||||
alias :include_last_read_post_number? :has_user_data
|
alias :include_last_read_post_number? :has_user_data
|
||||||
|
|
||||||
|
# TODO: For backwards compatibility with themes,
|
||||||
|
# Remove once Discourse 2.8 is released
|
||||||
def unread
|
def unread
|
||||||
unread_helper.unread_posts
|
0
|
||||||
end
|
end
|
||||||
alias :include_unread? :has_user_data
|
alias :include_unread? :has_user_data
|
||||||
|
|
||||||
|
# TODO: For backwards compatibility with themes,
|
||||||
|
# Remove once Discourse 2.8 is released
|
||||||
def new_posts
|
def new_posts
|
||||||
unread_helper.new_posts
|
unread_helper.unread_posts
|
||||||
end
|
end
|
||||||
alias :include_new_posts? :has_user_data
|
alias :include_new_posts? :has_user_data
|
||||||
|
|
||||||
|
def unread_posts
|
||||||
|
unread_helper.unread_posts
|
||||||
|
end
|
||||||
|
alias :include_unread_posts? :has_user_data
|
||||||
|
|
||||||
def include_excerpt?
|
def include_excerpt?
|
||||||
pinned || SiteSetting.always_include_topic_excerpts || theme_modifier_helper.serialize_topic_excerpts
|
pinned || SiteSetting.always_include_topic_excerpts || theme_modifier_helper.serialize_topic_excerpts
|
||||||
end
|
end
|
||||||
|
|
|
@ -752,7 +752,12 @@ class PostAlerter
|
||||||
|
|
||||||
DiscourseEvent.trigger(:before_create_notifications_for_users, notify, post)
|
DiscourseEvent.trigger(:before_create_notifications_for_users, notify, post)
|
||||||
|
|
||||||
already_seen_user_ids = Set.new TopicUser.where(topic_id: post.topic.id).where("highest_seen_post_number >= ?", post.post_number).pluck(:user_id)
|
already_seen_user_ids = Set.new(
|
||||||
|
TopicUser
|
||||||
|
.where(topic_id: post.topic.id)
|
||||||
|
.where("last_read_post_number >= ?", post.post_number)
|
||||||
|
.pluck(:user_id)
|
||||||
|
)
|
||||||
|
|
||||||
each_user_in_batches(notify) do |user|
|
each_user_in_batches(notify) do |user|
|
||||||
notification_type = !new_record && already_seen_user_ids.include?(user.id) ? Notification.types[:edited] : Notification.types[:posted]
|
notification_type = !new_record && already_seen_user_ids.include?(user.id) ? Notification.types[:edited] : Notification.types[:posted]
|
||||||
|
|
|
@ -2397,7 +2397,7 @@ en:
|
||||||
top: "There are no top topics."
|
top: "There are no top topics."
|
||||||
educate:
|
educate:
|
||||||
new: '<p>Your new topics will appear here. By default, topics are considered new and will show a <span class="badge new-topic badge-notification" style="vertical-align:middle;line-height:inherit;"></span> indicator if they were created in the last 2 days.</p><p>Visit your <a href="%{userPrefsUrl}">preferences</a> to change this.</p>'
|
new: '<p>Your new topics will appear here. By default, topics are considered new and will show a <span class="badge new-topic badge-notification" style="vertical-align:middle;line-height:inherit;"></span> indicator if they were created in the last 2 days.</p><p>Visit your <a href="%{userPrefsUrl}">preferences</a> to change this.</p>'
|
||||||
unread: '<p>Your unread topics appear here.</p><p>By default, topics are considered unread and will show unread counts <span class="badge new-posts badge-notification">1</span> if you:</p><ul><li>Created the topic</li><li>Replied to the topic</li><li>Read the topic for more than 4 minutes</li></ul><p>Or if you have explicitly set the topic to Tracked or Watched via the 🔔 in each topic.</p><p>Visit your <a href="%{userPrefsUrl}">preferences</a> to change this.</p>'
|
unread: '<p>Your unread topics appear here.</p><p>By default, topics are considered unread and will show unread counts <span class="badge unread-posts badge-notification">1</span> if you:</p><ul><li>Created the topic</li><li>Replied to the topic</li><li>Read the topic for more than 4 minutes</li></ul><p>Or if you have explicitly set the topic to Tracked or Watched via the 🔔 in each topic.</p><p>Visit your <a href="%{userPrefsUrl}">preferences</a> to change this.</p>'
|
||||||
bottom:
|
bottom:
|
||||||
latest: "There are no more latest topics."
|
latest: "There are no more latest topics."
|
||||||
posted: "There are no more posted topics."
|
posted: "There are no more posted topics."
|
||||||
|
@ -2457,15 +2457,9 @@ en:
|
||||||
not_found:
|
not_found:
|
||||||
title: "Topic not found"
|
title: "Topic not found"
|
||||||
description: "Sorry, we couldn't find that topic. Perhaps it was removed by a moderator?"
|
description: "Sorry, we couldn't find that topic. Perhaps it was removed by a moderator?"
|
||||||
total_unread_posts:
|
unread_posts:
|
||||||
one: "you have %{count} unread post in this topic"
|
one: "you have %{count} unread post in this topic"
|
||||||
other: "you have %{count} unread posts in this topic"
|
other: "you have %{count} unread posts in this topic"
|
||||||
unread_posts:
|
|
||||||
one: "you have %{count} unread old post in this topic"
|
|
||||||
other: "you have %{count} unread old posts in this topic"
|
|
||||||
new_posts:
|
|
||||||
one: "there is %{count} new post in this topic since you last read it"
|
|
||||||
other: "there are %{count} new posts in this topic since you last read it"
|
|
||||||
likes:
|
likes:
|
||||||
one: "there is %{count} like in this topic"
|
one: "there is %{count} like in this topic"
|
||||||
other: "there are %{count} likes in this topic"
|
other: "there are %{count} likes in this topic"
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'migration/column_dropper'
|
||||||
|
|
||||||
|
class RemoveHighestSeenPostNumberFromTopicUsers < ActiveRecord::Migration[6.1]
|
||||||
|
DROPPED_COLUMNS = {
|
||||||
|
topic_users: %i{highest_seen_post_number}
|
||||||
|
}
|
||||||
|
|
||||||
|
def up
|
||||||
|
DROPPED_COLUMNS.each do |table, columns|
|
||||||
|
Migration::ColumnDropper.execute_drop(table, columns)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
end
|
|
@ -646,7 +646,6 @@ class PostCreator
|
||||||
@topic.id,
|
@topic.id,
|
||||||
posted: true,
|
posted: true,
|
||||||
last_read_post_number: @post.post_number,
|
last_read_post_number: @post.post_number,
|
||||||
highest_seen_post_number: @post.post_number,
|
|
||||||
last_posted_at: Time.zone.now)
|
last_posted_at: Time.zone.now)
|
||||||
|
|
||||||
# assume it took us 5 seconds of reading time to make a post
|
# assume it took us 5 seconds of reading time to make a post
|
||||||
|
|
|
@ -57,8 +57,8 @@ def insert_topic_users
|
||||||
log "Inserting topic users..."
|
log "Inserting topic users..."
|
||||||
|
|
||||||
DB.exec <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, highest_seen_post_number, first_visited_at, last_visited_at, total_msecs_viewed)
|
INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, first_visited_at, last_visited_at, total_msecs_viewed)
|
||||||
SELECT user_id, topic_id, 't' , MAX(post_number), MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * #{MS_SPEND_CREATING_POST}
|
SELECT user_id, topic_id, 't' , MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * #{MS_SPEND_CREATING_POST}
|
||||||
FROM posts
|
FROM posts
|
||||||
WHERE user_id > 0
|
WHERE user_id > 0
|
||||||
GROUP BY user_id, topic_id
|
GROUP BY user_id, topic_id
|
||||||
|
|
|
@ -346,7 +346,6 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
||||||
["post_timings", "post_number"],
|
["post_timings", "post_number"],
|
||||||
["posts", "reply_to_post_number"],
|
["posts", "reply_to_post_number"],
|
||||||
["topic_users", "last_read_post_number"],
|
["topic_users", "last_read_post_number"],
|
||||||
["topic_users", "highest_seen_post_number"],
|
|
||||||
["topic_users", "last_emailed_post_number"],
|
["topic_users", "last_emailed_post_number"],
|
||||||
].each do |table, column|
|
].each do |table, column|
|
||||||
builder = DB.build <<~SQL
|
builder = DB.build <<~SQL
|
||||||
|
|
|
@ -72,7 +72,7 @@ class TopicsBulkAction
|
||||||
highest_number_source_column = @user.staff? ? 'highest_staff_post_number' : 'highest_post_number'
|
highest_number_source_column = @user.staff? ? 'highest_staff_post_number' : 'highest_post_number'
|
||||||
sql = <<~SQL
|
sql = <<~SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET highest_seen_post_number = t.#{highest_number_source_column} , last_read_post_number = t.#{highest_number_source_column}
|
SET last_read_post_number = t.#{highest_number_source_column}
|
||||||
FROM topics t
|
FROM topics t
|
||||||
WHERE t.id = tu.topic_id AND tu.user_id = :user_id AND t.id IN (:topic_ids)
|
WHERE t.id = tu.topic_id AND tu.user_id = :user_id AND t.id IN (:topic_ids)
|
||||||
SQL
|
SQL
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class Unread
|
class Unread
|
||||||
|
|
||||||
# This module helps us calculate unread and new post counts
|
# This module helps us calculate unread post counts
|
||||||
|
|
||||||
def initialize(topic, topic_user, guardian)
|
def initialize(topic, topic_user, guardian)
|
||||||
@guardian = guardian
|
@guardian = guardian
|
||||||
|
@ -11,29 +11,27 @@ class Unread
|
||||||
end
|
end
|
||||||
|
|
||||||
def unread_posts
|
def unread_posts
|
||||||
return 0 if do_not_notify?(@topic_user.notification_level)
|
return 0 if @topic_user.last_read_post_number.blank?
|
||||||
result = ((@topic_user.highest_seen_post_number || 0) - (@topic_user.last_read_post_number || 0))
|
|
||||||
result = 0 if result < 0
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_posts
|
|
||||||
return 0 if @topic_user.highest_seen_post_number.blank?
|
|
||||||
return 0 if do_not_notify?(@topic_user.notification_level)
|
return 0 if do_not_notify?(@topic_user.notification_level)
|
||||||
|
|
||||||
highest_post_number = @guardian.is_staff? ? @topic.highest_staff_post_number : @topic.highest_post_number
|
highest_post_number = @guardian.is_staff? ? @topic.highest_staff_post_number : @topic.highest_post_number
|
||||||
|
|
||||||
return 0 if (@topic_user.last_read_post_number || 0) > highest_post_number
|
return 0 if @topic_user.last_read_post_number > highest_post_number
|
||||||
|
|
||||||
new_posts = (highest_post_number - @topic_user.highest_seen_post_number)
|
unread = (highest_post_number - @topic_user.last_read_post_number)
|
||||||
new_posts = 0 if new_posts < 0
|
unread = 0 if unread < 0
|
||||||
new_posts
|
unread
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
DO_NOT_NOTIFY_LEVELS = [
|
||||||
|
TopicUser.notification_levels[:muted],
|
||||||
|
TopicUser.notification_levels[:regular]
|
||||||
|
]
|
||||||
|
|
||||||
def do_not_notify?(notification_level)
|
def do_not_notify?(notification_level)
|
||||||
[TopicUser.notification_levels[:muted], TopicUser.notification_levels[:regular]].include?(notification_level)
|
DO_NOT_NOTIFY_LEVELS.include?(notification_level)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -192,7 +192,7 @@ after_initialize do
|
||||||
return if topic_id.blank? || data[:track] != DiscourseNarrativeBot::NewUserNarrative.to_s
|
return if topic_id.blank? || data[:track] != DiscourseNarrativeBot::NewUserNarrative.to_s
|
||||||
|
|
||||||
topic_user = topic_users.find_by(topic_id: topic_id)
|
topic_user = topic_users.find_by(topic_id: topic_id)
|
||||||
return if topic_user.present? && (topic_user.last_read_post_number.present? || topic_user.highest_seen_post_number.present?)
|
return if topic_user.present? && topic_user.last_read_post_number.present?
|
||||||
|
|
||||||
topic = Topic.find_by(id: topic_id)
|
topic = Topic.find_by(id: topic_id)
|
||||||
return if topic.blank?
|
return if topic.blank?
|
||||||
|
|
|
@ -150,8 +150,7 @@ acceptance("Poll in a post reply history", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -205,8 +204,7 @@ acceptance("Poll in a post reply history", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 12,
|
last_read_post_number: 12,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -230,8 +230,7 @@ acceptance("Poll quote", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: true,
|
unpinned: true,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -275,8 +274,7 @@ acceptance("Poll quote", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -239,8 +239,7 @@ acceptance("Poll results", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 9,
|
last_read_post_number: 9,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: true,
|
unpinned: true,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -295,8 +294,7 @@ acceptance("Poll results", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -343,8 +341,7 @@ acceptance("Poll results", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
@ -389,8 +386,7 @@ acceptance("Poll results", function (needs) {
|
||||||
archetype: "regular",
|
archetype: "regular",
|
||||||
unseen: false,
|
unseen: false,
|
||||||
last_read_post_number: 12,
|
last_read_post_number: 12,
|
||||||
unread: 0,
|
unread_posts: 0,
|
||||||
new_posts: 0,
|
|
||||||
pinned: false,
|
pinned: false,
|
||||||
unpinned: null,
|
unpinned: null,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
|
@ -762,8 +762,8 @@ class ImportScripts::Base
|
||||||
puts "", "Updating topic users"
|
puts "", "Updating topic users"
|
||||||
|
|
||||||
DB.exec <<~SQL
|
DB.exec <<~SQL
|
||||||
INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, highest_seen_post_number, first_visited_at, last_visited_at, total_msecs_viewed)
|
INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, first_visited_at, last_visited_at, total_msecs_viewed)
|
||||||
SELECT user_id, topic_id, 't' , MAX(post_number), MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * 5000
|
SELECT user_id, topic_id, 't' , MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * 5000
|
||||||
FROM posts
|
FROM posts
|
||||||
WHERE user_id > 0
|
WHERE user_id > 0
|
||||||
GROUP BY user_id, topic_id
|
GROUP BY user_id, topic_id
|
||||||
|
|
|
@ -598,8 +598,7 @@ class ImportScripts::Telligent < ImportScripts::Base
|
||||||
# Mark all imported messages as read
|
# Mark all imported messages as read
|
||||||
DB.exec(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET last_read_post_number = t.highest_post_number,
|
SET last_read_post_number = t.highest_post_number
|
||||||
highest_seen_post_number = t.highest_post_number
|
|
||||||
FROM topics t
|
FROM topics t
|
||||||
JOIN topic_custom_fields tcf ON t.id = tcf.topic_id
|
JOIN topic_custom_fields tcf ON t.id = tcf.topic_id
|
||||||
WHERE tu.topic_id = t.id
|
WHERE tu.topic_id = t.id
|
||||||
|
|
|
@ -652,10 +652,6 @@ describe PostDestroyer do
|
||||||
it "sets the second user's last_read_post_number back to 1" do
|
it "sets the second user's last_read_post_number back to 1" do
|
||||||
expect(topic_user.last_read_post_number).to eq(1)
|
expect(topic_user.last_read_post_number).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets the second user's last_read_post_number back to 1" do
|
|
||||||
expect(topic_user.highest_seen_post_number).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,6 @@ describe TopicsBulkAction do
|
||||||
tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id)
|
tu = TopicUser.find_by(user_id: post1.user_id, topic_id: post1.topic_id)
|
||||||
|
|
||||||
expect(tu.last_read_post_number).to eq(3)
|
expect(tu.last_read_post_number).to eq(3)
|
||||||
expect(tu.highest_seen_post_number).to eq(3)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the user is staff" do
|
context "when the user is staff" do
|
||||||
|
@ -106,7 +105,6 @@ describe TopicsBulkAction do
|
||||||
tu = TopicUser.find_by(user_id: user.id, topic_id: post1.topic_id)
|
tu = TopicUser.find_by(user_id: user.id, topic_id: post1.topic_id)
|
||||||
|
|
||||||
expect(tu.last_read_post_number).to eq(4)
|
expect(tu.last_read_post_number).to eq(4)
|
||||||
expect(tu.highest_seen_post_number).to eq(4)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,62 +27,45 @@ describe Unread do
|
||||||
|
|
||||||
describe 'staff counts' do
|
describe 'staff counts' do
|
||||||
it 'should correctly return based on staff post number' do
|
it 'should correctly return based on staff post number' do
|
||||||
|
|
||||||
user.admin = true
|
user.admin = true
|
||||||
|
|
||||||
topic_user.last_read_post_number = 13
|
topic_user.last_read_post_number = 13
|
||||||
topic_user.highest_seen_post_number = 13
|
|
||||||
|
|
||||||
expect(unread.unread_posts).to eq(0)
|
expect(unread.unread_posts).to eq(2)
|
||||||
expect(unread.new_posts).to eq(2)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'unread_posts' do
|
describe 'unread_posts' do
|
||||||
it 'should have 0 unread posts if the user has seen all posts' do
|
it 'should have 0 unread posts if the user has read all posts' do
|
||||||
topic_user.last_read_post_number = 13
|
topic_user.last_read_post_number = 13
|
||||||
topic_user.highest_seen_post_number = 13
|
|
||||||
expect(unread.unread_posts).to eq(0)
|
expect(unread.unread_posts).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have 6 unread posts if the user has seen all but 6 posts' do
|
it 'returns the right unread posts for a user' do
|
||||||
topic_user.last_read_post_number = 5
|
topic_user.last_read_post_number = 10
|
||||||
topic_user.highest_seen_post_number = 11
|
expect(unread.unread_posts).to eq(3)
|
||||||
expect(unread.unread_posts).to eq(6)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should have 0 unread posts if the user has seen more posts than exist (deleted)' do
|
it 'returns the right unread posts for a staff user' do
|
||||||
topic_user.last_read_post_number = 100
|
user.admin = true
|
||||||
topic_user.highest_seen_post_number = 13
|
topic_user.last_read_post_number = 10
|
||||||
|
expect(unread.unread_posts).to eq(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have 0 unread posts if the user has read more posts than exist (deleted)' do
|
||||||
|
topic_user.last_read_post_number = 14
|
||||||
expect(unread.unread_posts).to eq(0)
|
expect(unread.unread_posts).to eq(0)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe 'new_posts' do
|
it 'has 0 unread posts if the user has read 10 posts but is not tracking' do
|
||||||
it 'should have 0 new posts if the user has read all posts' do
|
topic_user.last_read_post_number = 10
|
||||||
topic_user.last_read_post_number = 13
|
|
||||||
expect(unread.new_posts).to eq(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 0 when the topic is the same length as when you last saw it' do
|
|
||||||
topic_user.highest_seen_post_number = 13
|
|
||||||
expect(unread.new_posts).to eq(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 3 new posts if the user has read 10 posts' do
|
|
||||||
topic_user.highest_seen_post_number = 10
|
|
||||||
expect(unread.new_posts).to eq(3)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 0 new posts if the user has read 10 posts but is not tracking' do
|
|
||||||
topic_user.highest_seen_post_number = 10
|
|
||||||
topic_user.notification_level = TopicUser.notification_levels[:regular]
|
topic_user.notification_level = TopicUser.notification_levels[:regular]
|
||||||
expect(unread.new_posts).to eq(0)
|
expect(unread.unread_posts).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has 0 new posts if the user read more posts than exist (deleted)' do
|
it 'has 0 unread psots if the user has not seen the topic' do
|
||||||
topic_user.highest_seen_post_number = 16
|
topic_user.last_read_post_number = nil
|
||||||
expect(unread.new_posts).to eq(0)
|
expect(unread.unread_posts).to eq(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -425,7 +425,6 @@ describe PostMover do
|
||||||
bookmarked: true,
|
bookmarked: true,
|
||||||
notification_level: TopicUser.notification_levels[:watching],
|
notification_level: TopicUser.notification_levels[:watching],
|
||||||
last_read_post_number: 4,
|
last_read_post_number: 4,
|
||||||
highest_seen_post_number: 4,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
tu2 = Fabricate(
|
tu2 = Fabricate(
|
||||||
|
@ -435,7 +434,6 @@ describe PostMover do
|
||||||
bookmarked: true,
|
bookmarked: true,
|
||||||
notification_level: TopicUser.notification_levels[:watching],
|
notification_level: TopicUser.notification_levels[:watching],
|
||||||
last_read_post_number: 4,
|
last_read_post_number: 4,
|
||||||
highest_seen_post_number: 4,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -470,21 +468,18 @@ describe PostMover do
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user1,
|
user1,
|
||||||
last_read_post_number: 4,
|
last_read_post_number: 4,
|
||||||
highest_seen_post_number: 4,
|
|
||||||
last_emailed_post_number: 3,
|
last_emailed_post_number: 3,
|
||||||
notification_level: :tracking
|
notification_level: :tracking
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user2,
|
user2,
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 2,
|
last_emailed_post_number: 2,
|
||||||
notification_level: :tracking
|
notification_level: :tracking
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user3,
|
user3,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 4,
|
last_emailed_post_number: 4,
|
||||||
notification_level: :watching
|
notification_level: :watching
|
||||||
)
|
)
|
||||||
|
@ -496,28 +491,24 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: topic, user: user))
|
expect(TopicUser.find_by(topic: topic, user: user))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 4,
|
last_read_post_number: 4,
|
||||||
highest_seen_post_number: 4,
|
|
||||||
last_emailed_post_number: nil,
|
last_emailed_post_number: nil,
|
||||||
notification_level: TopicUser.notification_levels[:tracking]
|
notification_level: TopicUser.notification_levels[:tracking]
|
||||||
)
|
)
|
||||||
expect(TopicUser.find_by(topic: topic, user: user1))
|
expect(TopicUser.find_by(topic: topic, user: user1))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 4,
|
last_read_post_number: 4,
|
||||||
highest_seen_post_number: 4,
|
|
||||||
last_emailed_post_number: 3,
|
last_emailed_post_number: 3,
|
||||||
notification_level: TopicUser.notification_levels[:tracking]
|
notification_level: TopicUser.notification_levels[:tracking]
|
||||||
)
|
)
|
||||||
expect(TopicUser.find_by(topic: topic, user: user2))
|
expect(TopicUser.find_by(topic: topic, user: user2))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 2,
|
last_emailed_post_number: 2,
|
||||||
notification_level: TopicUser.notification_levels[:tracking]
|
notification_level: TopicUser.notification_levels[:tracking]
|
||||||
)
|
)
|
||||||
expect(TopicUser.find_by(topic: topic, user: user3))
|
expect(TopicUser.find_by(topic: topic, user: user3))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 4,
|
last_emailed_post_number: 4,
|
||||||
notification_level: TopicUser.notification_levels[:watching]
|
notification_level: TopicUser.notification_levels[:watching]
|
||||||
)
|
)
|
||||||
|
@ -526,7 +517,6 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: new_topic, user: user))
|
expect(TopicUser.find_by(topic: new_topic, user: user))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 1,
|
|
||||||
last_emailed_post_number: nil,
|
last_emailed_post_number: nil,
|
||||||
notification_level: TopicUser.notification_levels[:watching],
|
notification_level: TopicUser.notification_levels[:watching],
|
||||||
posted: true
|
posted: true
|
||||||
|
@ -534,7 +524,6 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: new_topic, user: user1))
|
expect(TopicUser.find_by(topic: new_topic, user: user1))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 2,
|
last_emailed_post_number: 2,
|
||||||
notification_level: TopicUser.notification_levels[:tracking],
|
notification_level: TopicUser.notification_levels[:tracking],
|
||||||
posted: false
|
posted: false
|
||||||
|
@ -542,7 +531,6 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: new_topic, user: user2))
|
expect(TopicUser.find_by(topic: new_topic, user: user2))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 2,
|
last_emailed_post_number: 2,
|
||||||
notification_level: TopicUser.notification_levels[:tracking],
|
notification_level: TopicUser.notification_levels[:tracking],
|
||||||
posted: true
|
posted: true
|
||||||
|
@ -550,7 +538,6 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: new_topic, user: user3))
|
expect(TopicUser.find_by(topic: new_topic, user: user3))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 2,
|
last_emailed_post_number: 2,
|
||||||
notification_level: TopicUser.notification_levels[:watching],
|
notification_level: TopicUser.notification_levels[:watching],
|
||||||
posted: false
|
posted: false
|
||||||
|
@ -810,52 +797,44 @@ describe PostMover do
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user1, topic,
|
user1, topic,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
highest_seen_post_number: 3,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user1, destination_topic,
|
user1, destination_topic,
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 1
|
last_emailed_post_number: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user2, topic,
|
user2, topic,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
highest_seen_post_number: 3,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
user2, destination_topic,
|
user2, destination_topic,
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 1,
|
|
||||||
last_emailed_post_number: 2
|
last_emailed_post_number: 2
|
||||||
)
|
)
|
||||||
|
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
admin1, topic,
|
admin1, topic,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
highest_seen_post_number: 3,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
admin1, destination_topic,
|
admin1, destination_topic,
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 3,
|
|
||||||
last_emailed_post_number: 1
|
last_emailed_post_number: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
admin2, topic,
|
admin2, topic,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
highest_seen_post_number: 3,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
create_topic_user(
|
create_topic_user(
|
||||||
admin2, destination_topic,
|
admin2, destination_topic,
|
||||||
last_read_post_number: 3,
|
last_read_post_number: 3,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 3
|
last_emailed_post_number: 3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -864,28 +843,24 @@ describe PostMover do
|
||||||
expect(TopicUser.find_by(topic: moved_to_topic, user: user1))
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user1))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 5,
|
|
||||||
last_emailed_post_number: 1
|
last_emailed_post_number: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(TopicUser.find_by(topic: moved_to_topic, user: user2))
|
expect(TopicUser.find_by(topic: moved_to_topic, user: user2))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
highest_seen_post_number: 1,
|
|
||||||
last_emailed_post_number: 5
|
last_emailed_post_number: 5
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(TopicUser.find_by(topic: moved_to_topic, user: admin1))
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin1))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 5,
|
|
||||||
last_emailed_post_number: 1
|
last_emailed_post_number: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(TopicUser.find_by(topic: moved_to_topic, user: admin2))
|
expect(TopicUser.find_by(topic: moved_to_topic, user: admin2))
|
||||||
.to have_attributes(
|
.to have_attributes(
|
||||||
last_read_post_number: 5,
|
last_read_post_number: 5,
|
||||||
highest_seen_post_number: 2,
|
|
||||||
last_emailed_post_number: 5
|
last_emailed_post_number: 5
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -895,14 +870,14 @@ describe PostMover do
|
||||||
|
|
||||||
original_topic_user1 = create_topic_user(
|
original_topic_user1 = create_topic_user(
|
||||||
user1, topic,
|
user1, topic,
|
||||||
highest_seen_post_number: 5,
|
last_read_post_number: 5,
|
||||||
first_visited_at: 5.hours.ago,
|
first_visited_at: 5.hours.ago,
|
||||||
last_visited_at: 30.minutes.ago,
|
last_visited_at: 30.minutes.ago,
|
||||||
notification_level: :tracking
|
notification_level: :tracking
|
||||||
).reload
|
).reload
|
||||||
destination_topic_user1 = create_topic_user(
|
destination_topic_user1 = create_topic_user(
|
||||||
user1, destination_topic,
|
user1, destination_topic,
|
||||||
highest_seen_post_number: 5,
|
last_read_post_number: 5,
|
||||||
first_visited_at: 7.hours.ago,
|
first_visited_at: 7.hours.ago,
|
||||||
last_visited_at: 2.hours.ago,
|
last_visited_at: 2.hours.ago,
|
||||||
notification_level: :watching
|
notification_level: :watching
|
||||||
|
@ -910,14 +885,14 @@ describe PostMover do
|
||||||
|
|
||||||
original_topic_user2 = create_topic_user(
|
original_topic_user2 = create_topic_user(
|
||||||
user2, topic,
|
user2, topic,
|
||||||
highest_seen_post_number: 5,
|
last_read_post_number: 5,
|
||||||
first_visited_at: 3.hours.ago,
|
first_visited_at: 3.hours.ago,
|
||||||
last_visited_at: 1.hour.ago,
|
last_visited_at: 1.hour.ago,
|
||||||
notification_level: :watching
|
notification_level: :watching
|
||||||
).reload
|
).reload
|
||||||
destination_topic_user2 = create_topic_user(
|
destination_topic_user2 = create_topic_user(
|
||||||
user2, destination_topic,
|
user2, destination_topic,
|
||||||
highest_seen_post_number: 5,
|
last_read_post_number: 5,
|
||||||
first_visited_at: 2.hours.ago,
|
first_visited_at: 2.hours.ago,
|
||||||
last_visited_at: 1.hour.ago,
|
last_visited_at: 1.hour.ago,
|
||||||
notification_level: :tracking
|
notification_level: :tracking
|
||||||
|
|
|
@ -20,12 +20,11 @@ describe PostTiming do
|
||||||
PostTiming.create!(topic_id: topic_id, user_id: user_id, post_number: post_number, msecs: 0)
|
PostTiming.create!(topic_id: topic_id, user_id: user_id, post_number: post_number, msecs: 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def topic_user(user_id, last_read_post_number, highest_seen_post_number)
|
def topic_user(user_id, last_read_post_number)
|
||||||
TopicUser.create!(
|
TopicUser.create!(
|
||||||
topic_id: topic_id,
|
topic_id: topic_id,
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
last_read_post_number: last_read_post_number,
|
last_read_post_number: last_read_post_number,
|
||||||
highest_seen_post_number: highest_seen_post_number
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,9 +36,9 @@ describe PostTiming do
|
||||||
timing(3, 2)
|
timing(3, 2)
|
||||||
timing(3, 3)
|
timing(3, 3)
|
||||||
|
|
||||||
_tu_one = topic_user(1, 1, 1)
|
_tu_one = topic_user(1, 1)
|
||||||
_tu_two = topic_user(2, 2, 2)
|
_tu_two = topic_user(2, 2)
|
||||||
_tu_three = topic_user(3, 3, 3)
|
_tu_three = topic_user(3, 3)
|
||||||
|
|
||||||
PostTiming.pretend_read(topic_id, 2, 3)
|
PostTiming.pretend_read(topic_id, 2, 3)
|
||||||
|
|
||||||
|
@ -49,15 +48,12 @@ describe PostTiming do
|
||||||
|
|
||||||
tu = TopicUser.find_by(topic_id: topic_id, user_id: 1)
|
tu = TopicUser.find_by(topic_id: topic_id, user_id: 1)
|
||||||
expect(tu.last_read_post_number).to eq(1)
|
expect(tu.last_read_post_number).to eq(1)
|
||||||
expect(tu.highest_seen_post_number).to eq(1)
|
|
||||||
|
|
||||||
tu = TopicUser.find_by(topic_id: topic_id, user_id: 2)
|
tu = TopicUser.find_by(topic_id: topic_id, user_id: 2)
|
||||||
expect(tu.last_read_post_number).to eq(3)
|
expect(tu.last_read_post_number).to eq(3)
|
||||||
expect(tu.highest_seen_post_number).to eq(3)
|
|
||||||
|
|
||||||
tu = TopicUser.find_by(topic_id: topic_id, user_id: 3)
|
tu = TopicUser.find_by(topic_id: topic_id, user_id: 3)
|
||||||
expect(tu.last_read_post_number).to eq(3)
|
expect(tu.last_read_post_number).to eq(3)
|
||||||
expect(tu.highest_seen_post_number).to eq(3)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1615,7 +1615,12 @@ describe Topic do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should generate the modified notification for the topic if already seen' do
|
it 'should generate the modified notification for the topic if already seen' do
|
||||||
TopicUser.create!(topic_id: topic.id, highest_seen_post_number: topic.posts.first.post_number, user_id: user.id)
|
TopicUser.create!(
|
||||||
|
topic_id: topic.id,
|
||||||
|
last_read_post_number: topic.posts.first.post_number,
|
||||||
|
user_id: user.id
|
||||||
|
)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
topic.change_category_to_id(new_category.id)
|
topic.change_category_to_id(new_category.id)
|
||||||
end.to change { Notification.count }.by(2)
|
end.to change { Notification.count }.by(2)
|
||||||
|
|
|
@ -597,7 +597,6 @@ describe TopicTrackingState do
|
||||||
tracking = {
|
tracking = {
|
||||||
notification_level: TopicUser.notification_levels[:tracking],
|
notification_level: TopicUser.notification_levels[:tracking],
|
||||||
last_read_post_number: 1,
|
last_read_post_number: 1,
|
||||||
highest_seen_post_number: 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TopicUser.change(user.id, post1.topic_id, tracking)
|
TopicUser.change(user.id, post1.topic_id, tracking)
|
||||||
|
|
|
@ -434,7 +434,7 @@ describe TopicUser do
|
||||||
p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2)
|
p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2)
|
||||||
p1.topic.notifier.watch_topic!(p1.user_id)
|
p1.topic.notifier.watch_topic!(p1.user_id)
|
||||||
|
|
||||||
DB.exec("UPDATE topic_users set highest_seen_post_number=1, last_read_post_number=0
|
DB.exec("UPDATE topic_users set last_read_post_number=0
|
||||||
WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id)
|
WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id)
|
||||||
|
|
||||||
[p1, p2].each do |p|
|
[p1, p2].each do |p|
|
||||||
|
@ -445,7 +445,6 @@ describe TopicUser do
|
||||||
|
|
||||||
tu = TopicUser.find_by(user_id: p1.user_id, topic_id: p1.topic_id)
|
tu = TopicUser.find_by(user_id: p1.user_id, topic_id: p1.topic_id)
|
||||||
expect(tu.last_read_post_number).to eq(p2.post_number)
|
expect(tu.last_read_post_number).to eq(p2.post_number)
|
||||||
expect(tu.highest_seen_post_number).to eq(2)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,7 @@ describe 'private messages' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :string, nullable: true },
|
unpinned: { type: :string, nullable: true },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
@ -174,8 +173,7 @@ describe 'private messages' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :string, nullable: true },
|
unpinned: { type: :string, nullable: true },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
|
|
@ -277,8 +277,7 @@ describe 'tags' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :string, nullable: true },
|
unpinned: { type: :string, nullable: true },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
|
|
@ -225,8 +225,7 @@ describe 'topics' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :boolean },
|
unpinned: { type: :boolean },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
@ -603,8 +602,7 @@ describe 'topics' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :string, nullable: true },
|
unpinned: { type: :string, nullable: true },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
@ -704,8 +702,7 @@ describe 'topics' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :boolean },
|
unpinned: { type: :boolean },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
@ -807,8 +804,7 @@ describe 'topics' do
|
||||||
archetype: { type: :string },
|
archetype: { type: :string },
|
||||||
unseen: { type: :boolean },
|
unseen: { type: :boolean },
|
||||||
last_read_post_number: { type: :integer },
|
last_read_post_number: { type: :integer },
|
||||||
unread: { type: :integer },
|
unread_posts: { type: :integer },
|
||||||
new_posts: { type: :integer },
|
|
||||||
pinned: { type: :boolean },
|
pinned: { type: :boolean },
|
||||||
unpinned: { type: :boolean },
|
unpinned: { type: :boolean },
|
||||||
visible: { type: :boolean },
|
visible: { type: :boolean },
|
||||||
|
|
|
@ -1010,7 +1010,6 @@ RSpec.describe TopicsController do
|
||||||
|
|
||||||
topic_user.update!(
|
topic_user.update!(
|
||||||
last_read_post_number: 2,
|
last_read_post_number: 2,
|
||||||
highest_seen_post_number: 2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# ensure we have 2 notifications
|
# ensure we have 2 notifications
|
||||||
|
@ -1036,7 +1035,7 @@ RSpec.describe TopicsController do
|
||||||
expect(PostTiming.where(topic: topic, user: user, post_number: 2).exists?).to eq(false)
|
expect(PostTiming.where(topic: topic, user: user, post_number: 2).exists?).to eq(false)
|
||||||
expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(true)
|
expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(true)
|
||||||
|
|
||||||
expect(TopicUser.where(topic: topic, user: user, last_read_post_number: 1, highest_seen_post_number: 1).exists?).to eq(true)
|
expect(TopicUser.where(topic: topic, user: user, last_read_post_number: 1).exists?).to eq(true)
|
||||||
|
|
||||||
user.user_stat.reload
|
user.user_stat.reload
|
||||||
expect(user.user_stat.first_unread_at).to eq_time(topic.updated_at)
|
expect(user.user_stat.first_unread_at).to eq_time(topic.updated_at)
|
||||||
|
@ -1051,7 +1050,7 @@ RSpec.describe TopicsController do
|
||||||
delete "/t/#{topic.id}/timings.json?last=1"
|
delete "/t/#{topic.id}/timings.json?last=1"
|
||||||
|
|
||||||
expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(false)
|
expect(PostTiming.where(topic: topic, user: user, post_number: 1).exists?).to eq(false)
|
||||||
expect(TopicUser.where(topic: topic, user: user, last_read_post_number: nil, highest_seen_post_number: nil).exists?).to eq(true)
|
expect(TopicUser.where(topic: topic, user: user, last_read_post_number: nil).exists?).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1259,7 +1259,13 @@ describe PostAlerter do
|
||||||
fab!(:category) { Fabricate(:category) }
|
fab!(:category) { Fabricate(:category) }
|
||||||
|
|
||||||
it 'creates single edit notification when post is modified' do
|
it 'creates single edit notification when post is modified' do
|
||||||
TopicUser.create!(user_id: user.id, topic_id: topic.id, notification_level: TopicUser.notification_levels[:watching], highest_seen_post_number: post.post_number)
|
TopicUser.create!(
|
||||||
|
user_id: user.id,
|
||||||
|
topic_id: topic.id,
|
||||||
|
notification_level: TopicUser.notification_levels[:watching],
|
||||||
|
last_read_post_number: post.post_number
|
||||||
|
)
|
||||||
|
|
||||||
PostRevisor.new(post).revise!(last_editor, tags: [tag.name])
|
PostRevisor.new(post).revise!(last_editor, tags: [tag.name])
|
||||||
PostAlerter.new.notify_post_users(post, [])
|
PostAlerter.new.notify_post_users(post, [])
|
||||||
expect(Notification.count).to eq(1)
|
expect(Notification.count).to eq(1)
|
||||||
|
@ -1280,7 +1286,7 @@ describe PostAlerter do
|
||||||
category: category.id
|
category: category.id
|
||||||
)
|
)
|
||||||
|
|
||||||
TopicUser.change(user, post.topic_id, highest_seen_post_number: post.post_number)
|
TopicUser.change(user, post.topic_id, last_read_post_number: post.post_number)
|
||||||
|
|
||||||
# Manually run job after the user read the topic to simulate a slow
|
# Manually run job after the user read the topic to simulate a slow
|
||||||
# Sidekiq.
|
# Sidekiq.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user