mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 12:12:26 +08:00
more progress towards live unread and new counts, unread message implemented, still to implement delete messages
This commit is contained in:
parent
f2da06a78f
commit
e93b7a3b20
|
@ -35,7 +35,7 @@ Discourse.ListController = Discourse.Controller.extend({
|
|||
var listController = this;
|
||||
this.set('loading', true);
|
||||
|
||||
var trackingState = Discourse.get('currentUser.userTrackingState');
|
||||
var trackingState = Discourse.TopicTrackingState.current();
|
||||
|
||||
if (filterMode === 'categories') {
|
||||
return Discourse.CategoryList.list(filterMode).then(function(items) {
|
||||
|
|
|
@ -55,7 +55,7 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
|
|||
|
||||
// Show newly inserted topics
|
||||
showInserted: function(e) {
|
||||
var tracker = Discourse.get('currentUser.userTrackingState');
|
||||
var tracker = Discourse.TopicTrackingState.current();
|
||||
|
||||
// Move inserted into topics
|
||||
this.get('content').loadBefore(tracker.get('newIncoming'));
|
||||
|
|
|
@ -10,7 +10,10 @@ var validNavNames = ['latest', 'hot', 'categories', 'category', 'favorited', 'un
|
|||
var validAnon = ['latest', 'hot', 'categories', 'category'];
|
||||
|
||||
Discourse.NavItem = Discourse.Model.extend({
|
||||
userTrackingStateBinding: Ember.Binding.oneWay('Discourse.currentUser.userTrackingState.messageCount'),
|
||||
topicTrackingState: function(){
|
||||
return Discourse.TopicTrackingState.current();
|
||||
}.property(),
|
||||
|
||||
categoryName: function() {
|
||||
var split = this.get('name').split('/');
|
||||
return split[0] === 'category' ? split[1] : null;
|
||||
|
@ -25,11 +28,11 @@ Discourse.NavItem = Discourse.Model.extend({
|
|||
}.property('name'),
|
||||
|
||||
count: function() {
|
||||
var state = Discourse.get('currentUser.userTrackingState');
|
||||
var state = this.get('topicTrackingState');
|
||||
if (state) {
|
||||
return state.lookupCount(this.get('name'));
|
||||
}
|
||||
}.property('userTrackingState')
|
||||
}.property('topicTrackingState.messageCount')
|
||||
});
|
||||
|
||||
Discourse.NavItem.reopenClass({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Discourse.UserTrackingState = Discourse.Model.extend({
|
||||
Discourse.TopicTrackingState = Discourse.Model.extend({
|
||||
messageCount: 0,
|
||||
|
||||
init: function(){
|
||||
|
@ -17,7 +17,7 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
tracker.removeTopic(data.topic_id);
|
||||
}
|
||||
|
||||
if (data.message_type === "new_topic") {
|
||||
if (data.message_type === "new_topic" || data.message_type === "unread") {
|
||||
tracker.states["t" + data.topic_id] = data.payload;
|
||||
tracker.notify(data);
|
||||
}
|
||||
|
@ -26,7 +26,10 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
};
|
||||
|
||||
Discourse.MessageBus.subscribe("/new", process);
|
||||
Discourse.MessageBus.subscribe("/unread/" + Discourse.currentUser.id, process);
|
||||
var currentUser = Discourse.User.current();
|
||||
if(currentUser) {
|
||||
Discourse.MessageBus.subscribe("/unread/" + currentUser.id, process);
|
||||
}
|
||||
},
|
||||
|
||||
notify: function(data){
|
||||
|
@ -62,6 +65,8 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
sync: function(list, filter){
|
||||
var tracker = this;
|
||||
|
||||
if(!list || !list.topics || !list.topics.length) { return; }
|
||||
|
||||
if(filter === "new" && !list.more_topics_url){
|
||||
// scrub all new rows and reload from list
|
||||
$.each(this.states, function(){
|
||||
|
@ -88,8 +93,10 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
if(topic.unseen) {
|
||||
row.last_read_post_number = null;
|
||||
} else {
|
||||
row.last_read_post_number = topic.last_read_post_number;
|
||||
// subtle issue here
|
||||
row.last_read_post_number = topic.last_read_post_number || topic.highest_post_number;
|
||||
}
|
||||
|
||||
row.highest_post_number = topic.highest_post_number;
|
||||
if (topic.category) {
|
||||
row.category_name = topic.category.name;
|
||||
|
@ -97,6 +104,8 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
|
||||
if (row.last_read_post_number === null || row.highest_post_number > row.last_read_post_number) {
|
||||
tracker.states["t" + topic.id] = row;
|
||||
} else {
|
||||
delete tracker.states["t" + topic.id];
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -151,18 +160,28 @@ Discourse.UserTrackingState = Discourse.Model.extend({
|
|||
// not exposed
|
||||
var states = this.states;
|
||||
|
||||
data.each(function(row){
|
||||
states["t" + row.topic_id] = row;
|
||||
});
|
||||
if(data) {
|
||||
data.each(function(row){
|
||||
states["t" + row.topic_id] = row;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Discourse.UserTrackingState.reopenClass({
|
||||
Discourse.TopicTrackingState.reopenClass({
|
||||
createFromStates: function(data){
|
||||
var instance = Discourse.UserTrackingState.create();
|
||||
var instance = Discourse.TopicTrackingState.create();
|
||||
instance.loadStates(data);
|
||||
instance.establishChannels();
|
||||
return instance;
|
||||
},
|
||||
current: function(){
|
||||
if (!this.tracker) {
|
||||
var data = PreloadStore.get('topicTrackingStates');
|
||||
this.tracker = this.createFromStates(data);
|
||||
PreloadStore.remove('topicTrackingStates');
|
||||
}
|
||||
return this.tracker;
|
||||
}
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
The base Application route
|
||||
|
||||
@class ApplicationRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ApplicationRoute = Discourse.Route.extend({
|
||||
setupController: function(controller) {
|
||||
Discourse.set('site', Discourse.Site.create(PreloadStore.get('site')));
|
||||
var currentUser = PreloadStore.get('currentUser');
|
||||
if (currentUser) {
|
||||
var states = currentUser.user_tracking_states;
|
||||
currentUser.user_tracking_states = null;
|
||||
|
||||
Discourse.set('currentUser', Discourse.User.create(currentUser));
|
||||
Discourse.set('currentUser.userTrackingState', Discourse.UserTrackingState.createFromStates(states));
|
||||
}
|
||||
// make sure we delete preloaded data
|
||||
PreloadStore.remove('site');
|
||||
PreloadStore.remove('currentUser');
|
||||
}
|
||||
});
|
|
@ -28,12 +28,12 @@
|
|||
</tr>
|
||||
</thead>
|
||||
|
||||
{{#if Discourse.currentUser.userTrackingState.hasIncoming}}
|
||||
{{#if view.topicTrackingState.hasIncoming}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="9">
|
||||
<div class='alert alert-info'>
|
||||
{{countI18n new_topics_inserted countBinding="Discourse.currentUser.userTrackingState.incomingCount"}}
|
||||
{{countI18n new_topics_inserted countBinding="view.topicTrackingState.incomingCount"}}
|
||||
<a href='#' {{action showInserted}}>{{i18n show_new_topics}}</a>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -14,6 +14,9 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
|
|||
listBinding: 'controller.model',
|
||||
loadedMore: false,
|
||||
currentTopicId: null,
|
||||
topicTrackingState: function() {
|
||||
return Discourse.TopicTrackingState.current();
|
||||
}.property(),
|
||||
|
||||
willDestroyElement: function() {
|
||||
this.unbindScrolling();
|
||||
|
@ -42,8 +45,11 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
|
|||
},
|
||||
|
||||
showTable: function() {
|
||||
return this.get('list.topics').length > 0 || Discourse.get('currentUser.userTrackingState.hasIncoming');
|
||||
}.property('list.topics','Discourse.currentUser.userTrackingState.hasIncoming'),
|
||||
var topics = this.get('list.topics');
|
||||
if(topics) {
|
||||
return this.get('list.topics').length > 0 || this.get('topicTrackingState.hasIncoming');
|
||||
}
|
||||
}.property('list.topics','topicTrackingState.hasIncoming'),
|
||||
|
||||
loadMore: function() {
|
||||
var listTopicsView = this;
|
||||
|
|
|
@ -107,6 +107,9 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
if current_user.present?
|
||||
store_preloaded("currentUser", MultiJson.dump(CurrentUserSerializer.new(current_user, root: false)))
|
||||
|
||||
serializer = ActiveModel::ArraySerializer.new(TopicTrackingState.report([current_user.id]), each_serializer: TopicTrackingStateSerializer)
|
||||
store_preloaded("topicTrackingStates", MultiJson.dump(serializer))
|
||||
end
|
||||
store_preloaded("siteSettings", SiteSetting.client_settings_json)
|
||||
end
|
||||
|
|
|
@ -133,6 +133,12 @@ class Category < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def secure_group_ids
|
||||
if self.secure
|
||||
groups.pluck("groups.id")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# the allows end users to always know which topics have unread posts in them
|
||||
# and which topics are new
|
||||
|
||||
class UserTrackingState
|
||||
class TopicTrackingState
|
||||
|
||||
include ActiveModel::SerializerSupport
|
||||
|
||||
|
@ -11,16 +11,53 @@ class UserTrackingState
|
|||
|
||||
attr_accessor :user_id, :topic_id, :highest_post_number, :last_read_post_number, :created_at, :category_name
|
||||
|
||||
MessageBus.client_filter(CHANNEL) do |user_id, message|
|
||||
if user_id
|
||||
UserTrackingState.new(User.find(user_id)).filter(message)
|
||||
else
|
||||
nil
|
||||
def self.publish_new(topic)
|
||||
|
||||
message = {
|
||||
topic_id: topic.id,
|
||||
message_type: "new_topic",
|
||||
payload: {
|
||||
last_read_post_number: nil,
|
||||
highest_post_number: 1,
|
||||
created_at: topic.created_at,
|
||||
topic_id: topic.id
|
||||
}
|
||||
}
|
||||
|
||||
group_ids = topic.category && topic.category.secure_group_ids
|
||||
|
||||
MessageBus.publish("/new", message.as_json, group_ids: group_ids)
|
||||
publish_read(topic.id, 1, topic.user_id)
|
||||
end
|
||||
|
||||
def self.publish_unread(post)
|
||||
# TODO at high scale we are going to have to defer this,
|
||||
# perhaps cut down to users that are around in the last 7 days as well
|
||||
#
|
||||
group_ids = post.topic.category && post.topic.category.secure_group_ids
|
||||
|
||||
TopicUser
|
||||
.tracking(post.topic_id)
|
||||
.select([:user_id,:last_read_post_number])
|
||||
.each do |tu|
|
||||
|
||||
message = {
|
||||
topic_id: post.topic_id,
|
||||
message_type: "unread",
|
||||
payload: {
|
||||
last_read_post_number: tu.last_read_post_number,
|
||||
highest_post_number: post.post_number,
|
||||
created_at: post.created_at,
|
||||
topic_id: post.topic_id
|
||||
}
|
||||
}
|
||||
|
||||
MessageBus.publish("/unread/#{tu.user_id}", message.as_json, group_ids: group_ids)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def self.trigger_change(topic_id, post_number, user_id=nil)
|
||||
MessageBus.publish(CHANNEL, "CHANGE", user_ids: [user_id].compact)
|
||||
def self.publish_read(topic_id, highest_post_number, user_id)
|
||||
end
|
||||
|
||||
def self.treat_as_new_topic_clause
|
||||
|
@ -76,7 +113,7 @@ SQL
|
|||
end
|
||||
|
||||
SqlBuilder.new(sql)
|
||||
.map_exec(UserTrackingState, user_ids: user_ids, topic_id: topic_id)
|
||||
.map_exec(TopicTrackingState, user_ids: user_ids, topic_id: topic_id)
|
||||
|
||||
end
|
||||
|
|
@ -5,6 +5,12 @@ class TopicUser < ActiveRecord::Base
|
|||
scope :starred_since, lambda { |sinceDaysAgo| where('starred_at > ?', sinceDaysAgo.days.ago) }
|
||||
scope :by_date_starred, group('date(starred_at)').order('date(starred_at)')
|
||||
|
||||
scope :tracking, lambda { |topic_id|
|
||||
where(topic_id: topic_id)
|
||||
.where("COALESCE(topic_users.notification_level, :regular) >= :tracking",
|
||||
regular: TopicUser.notification_levels[:regular], tracking: TopicUser.notification_levels[:tracking])
|
||||
}
|
||||
|
||||
# Class methods
|
||||
class << self
|
||||
|
||||
|
|
|
@ -576,9 +576,6 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def user_tracking_states
|
||||
UserTrackingState.report([self.id])
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
|
|
|
@ -14,10 +14,6 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||
:external_links_in_new_tab,
|
||||
:trust_level
|
||||
|
||||
has_many :user_tracking_states, serializer: UserTrackingStateSerializer, embed: :objects
|
||||
|
||||
# we probably want to move this into site, but that json is cached so hanging it off current user seems okish
|
||||
|
||||
def include_site_flagged_posts_count?
|
||||
object.staff?
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class UserTrackingStateSerializer < ApplicationSerializer
|
||||
class TopicTrackingStateSerializer < ApplicationSerializer
|
||||
attributes :topic_id, :highest_post_number, :last_read_post_number, :created_at, :category_name
|
||||
end
|
|
@ -123,7 +123,6 @@ class PostCreator
|
|||
|
||||
@user.last_posted_at = post.created_at
|
||||
@user.save!
|
||||
|
||||
if post.post_number > 1
|
||||
MessageBus.publish("/topic/#{post.topic_id}",{
|
||||
id: post.id,
|
||||
|
@ -142,12 +141,14 @@ class PostCreator
|
|||
post.save_reply_relationships
|
||||
end
|
||||
|
||||
# We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
|
||||
# been comitted.
|
||||
topic_id = @opts[:topic_id] || topic.try(:id)
|
||||
Jobs.enqueue(:feature_topic_users, topic_id: topic.id) if topic_id.present?
|
||||
if post
|
||||
if post && !post.errors.present?
|
||||
|
||||
# We need to enqueue jobs after the transaction. Otherwise they might begin before the data has
|
||||
# been comitted.
|
||||
topic_id = @opts[:topic_id] || topic.try(:id)
|
||||
Jobs.enqueue(:feature_topic_users, topic_id: topic.id) if topic_id.present?
|
||||
post.trigger_post_process
|
||||
after_post_create(post)
|
||||
after_topic_create(topic) if new_topic
|
||||
end
|
||||
|
||||
|
@ -164,7 +165,13 @@ class PostCreator
|
|||
|
||||
def secure_group_ids(topic)
|
||||
@secure_group_ids ||= if topic.category && topic.category.secure?
|
||||
topic.category.groups.select("groups.id").map{|g| g.id}
|
||||
topic.category.secure_group_ids
|
||||
end
|
||||
end
|
||||
|
||||
def after_post_create(post)
|
||||
if post.post_number > 1
|
||||
TopicTrackingState.publish_unread(post)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -177,21 +184,8 @@ class PostCreator
|
|||
|
||||
topic.posters = topic.posters_summary
|
||||
topic.posts_count = 1
|
||||
topic_json = TopicListItemSerializer.new(topic).as_json
|
||||
|
||||
message = {
|
||||
topic_id: topic.id,
|
||||
message_type: "new_topic",
|
||||
payload: {
|
||||
last_read_post_number: nil,
|
||||
topic_id: topic.id
|
||||
}
|
||||
}
|
||||
|
||||
group_ids = secure_group_ids(topic)
|
||||
MessageBus.publish("/new", message.as_json, group_ids: group_ids)
|
||||
|
||||
# TODO post creator should get an unread
|
||||
TopicTrackingState.publish_new(topic)
|
||||
end
|
||||
|
||||
def create_topic
|
||||
|
|
122
script/alice.txt
Normal file
122
script/alice.txt
Normal file
|
@ -0,0 +1,122 @@
|
|||
Alice was beginning to get very tired of sitting by her sister on the
|
||||
bank, and of having nothing to do: once or twice she had peeped into the
|
||||
book her sister was reading, but it had no pictures or conversations in
|
||||
it, 'and what is the use of a book,' thought Alice 'without pictures or
|
||||
conversation?'
|
||||
|
||||
So she was considering in her own mind (as well as she could, for the
|
||||
hot day made her feel very sleepy and stupid), whether the pleasure
|
||||
of making a daisy-chain would be worth the trouble of getting up and
|
||||
picking the daisies, when suddenly a White Rabbit with pink eyes ran
|
||||
close by her.
|
||||
|
||||
There was nothing so VERY remarkable in that; nor did Alice think it so
|
||||
VERY much out of the way to hear the Rabbit say to itself, 'Oh dear!
|
||||
Oh dear! I shall be late!' (when she thought it over afterwards, it
|
||||
occurred to her that she ought to have wondered at this, but at the time
|
||||
it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
|
||||
OUT OF ITS WAISTCOAT-POCKET, and looked at it, and then hurried on,
|
||||
Alice started to her feet, for it flashed across her mind that she had
|
||||
never before seen a rabbit with either a waistcoat-pocket, or a watch
|
||||
to take out of it, and burning with curiosity, she ran across the field
|
||||
after it, and fortunately was just in time to see it pop down a large
|
||||
rabbit-hole under the hedge.
|
||||
|
||||
In another moment down went Alice after it, never once considering how
|
||||
in the world she was to get out again.
|
||||
|
||||
The rabbit-hole went straight on like a tunnel for some way, and then
|
||||
dipped suddenly down, so suddenly that Alice had not a moment to think
|
||||
about stopping herself before she found herself falling down a very deep
|
||||
well.
|
||||
|
||||
Either the well was very deep, or she fell very slowly, for she had
|
||||
plenty of time as she went down to look about her and to wonder what was
|
||||
going to happen next. First, she tried to look down and make out what
|
||||
she was coming to, but it was too dark to see anything; then she
|
||||
looked at the sides of the well, and noticed that they were filled with
|
||||
cupboards and book-shelves; here and there she saw maps and pictures
|
||||
hung upon pegs. She took down a jar from one of the shelves as
|
||||
she passed; it was labelled 'ORANGE MARMALADE', but to her great
|
||||
disappointment it was empty: she did not like to drop the jar for fear
|
||||
of killing somebody, so managed to put it into one of the cupboards as
|
||||
she fell past it.
|
||||
|
||||
'Well!' thought Alice to herself, 'after such a fall as this, I shall
|
||||
think nothing of tumbling down stairs! How brave they'll all think me at
|
||||
home! Why, I wouldn't say anything about it, even if I fell off the top
|
||||
of the house!' (Which was very likely true.)
|
||||
|
||||
Down, down, down. Would the fall NEVER come to an end! 'I wonder how
|
||||
many miles I've fallen by this time?' she said aloud. 'I must be getting
|
||||
somewhere near the centre of the earth. Let me see: that would be four
|
||||
thousand miles down, I think--' (for, you see, Alice had learnt several
|
||||
things of this sort in her lessons in the schoolroom, and though this
|
||||
was not a VERY good opportunity for showing off her knowledge, as there
|
||||
was no one to listen to her, still it was good practice to say it over)
|
||||
'--yes, that's about the right distance--but then I wonder what Latitude
|
||||
or Longitude I've got to?' (Alice had no idea what Latitude was, or
|
||||
Longitude either, but thought they were nice grand words to say.)
|
||||
|
||||
Presently she began again. 'I wonder if I shall fall right THROUGH the
|
||||
earth! How funny it'll seem to come out among the people that walk with
|
||||
their heads downward! The Antipathies, I think--' (she was rather glad
|
||||
there WAS no one listening, this time, as it didn't sound at all the
|
||||
right word) '--but I shall have to ask them what the name of the country
|
||||
is, you know. Please, Ma'am, is this New Zealand or Australia?' (and
|
||||
she tried to curtsey as she spoke--fancy CURTSEYING as you're falling
|
||||
through the air! Do you think you could manage it?) 'And what an
|
||||
ignorant little girl she'll think me for asking! No, it'll never do to
|
||||
ask: perhaps I shall see it written up somewhere.'
|
||||
|
||||
Down, down, down. There was nothing else to do, so Alice soon began
|
||||
talking again. 'Dinah'll miss me very much to-night, I should think!'
|
||||
(Dinah was the cat.) 'I hope they'll remember her saucer of milk at
|
||||
tea-time. Dinah my dear! I wish you were down here with me! There are no
|
||||
mice in the air, I'm afraid, but you might catch a bat, and that's very
|
||||
like a mouse, you know. But do cats eat bats, I wonder?' And here Alice
|
||||
began to get rather sleepy, and went on saying to herself, in a dreamy
|
||||
sort of way, 'Do cats eat bats? Do cats eat bats?' and sometimes, 'Do
|
||||
bats eat cats?' for, you see, as she couldn't answer either question,
|
||||
it didn't much matter which way she put it. She felt that she was dozing
|
||||
off, and had just begun to dream that she was walking hand in hand with
|
||||
Dinah, and saying to her very earnestly, 'Now, Dinah, tell me the truth:
|
||||
did you ever eat a bat?' when suddenly, thump! thump! down she came upon
|
||||
a heap of sticks and dry leaves, and the fall was over.
|
||||
|
||||
Alice was not a bit hurt, and she jumped up on to her feet in a moment:
|
||||
she looked up, but it was all dark overhead; before her was another
|
||||
long passage, and the White Rabbit was still in sight, hurrying down it.
|
||||
There was not a moment to be lost: away went Alice like the wind, and
|
||||
was just in time to hear it say, as it turned a corner, 'Oh my ears
|
||||
and whiskers, how late it's getting!' She was close behind it when she
|
||||
turned the corner, but the Rabbit was no longer to be seen: she found
|
||||
herself in a long, low hall, which was lit up by a row of lamps hanging
|
||||
from the roof.
|
||||
|
||||
There were doors all round the hall, but they were all locked; and when
|
||||
Alice had been all the way down one side and up the other, trying every
|
||||
door, she walked sadly down the middle, wondering how she was ever to
|
||||
get out again.
|
||||
|
||||
Suddenly she came upon a little three-legged table, all made of solid
|
||||
glass; there was nothing on it except a tiny golden key, and Alice's
|
||||
first thought was that it might belong to one of the doors of the hall;
|
||||
but, alas! either the locks were too large, or the key was too small,
|
||||
but at any rate it would not open any of them. However, on the second
|
||||
time round, she came upon a low curtain she had not noticed before, and
|
||||
behind it was a little door about fifteen inches high: she tried the
|
||||
little golden key in the lock, and to her great delight it fitted!
|
||||
|
||||
Alice opened the door and found that it led into a small passage, not
|
||||
much larger than a rat-hole: she knelt down and looked along the passage
|
||||
into the loveliest garden you ever saw. How she longed to get out of
|
||||
that dark hall, and wander about among those beds of bright flowers and
|
||||
those cool fountains, but she could not even get her head through the
|
||||
doorway; 'and even if my head would go through,' thought poor Alice, 'it
|
||||
would be of very little use without my shoulders. Oh, how I wish I could
|
||||
shut up like a telescope! I think I could, if I only know how to begin.'
|
||||
For, you see, so many out-of-the-way things had happened lately,
|
||||
that Alice had begun to think that very few things indeed were really
|
||||
impossible.
|
||||
|
60
script/user_simulator.rb
Normal file
60
script/user_simulator.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
# used during local testing, simulates a user active on the site.
|
||||
#
|
||||
# by default 1 new topic every 30 sec, 1 reply to last topic every 30 secs
|
||||
|
||||
require 'optparse'
|
||||
require 'gabbler'
|
||||
|
||||
user_id = nil
|
||||
|
||||
def sentence
|
||||
@gabbler ||= Gabbler.new.tap do |gabbler|
|
||||
story = File.read(File.dirname(__FILE__) + "/alice.txt")
|
||||
gabbler.learn(story)
|
||||
end
|
||||
|
||||
sentence = ""
|
||||
until sentence.length > 800 do
|
||||
sentence << @gabbler.sentence
|
||||
sentence << "\n"
|
||||
end
|
||||
sentence
|
||||
end
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: ruby user_simulator.rb [options]"
|
||||
opts.on("-u", "--user NUMBER", "user id") do |u|
|
||||
user_id = u.to_i
|
||||
end
|
||||
end.parse!
|
||||
|
||||
unless user_id
|
||||
puts "user must be specified"
|
||||
exit
|
||||
end
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
||||
|
||||
unless Rails.env.development?
|
||||
puts "Bad idea to run a script that inserts random posts in any non development environment"
|
||||
exit
|
||||
end
|
||||
|
||||
|
||||
user = User.find(user_id)
|
||||
last_topics = Topic.order('id desc').limit(10).pluck(:id)
|
||||
|
||||
puts "Simulating activity for user id #{user.id}: #{user.name}"
|
||||
|
||||
|
||||
while true
|
||||
# puts "Creating a random topi"
|
||||
|
||||
# category = Category.where(secure: false).order('random()').first
|
||||
# PostCreator.create(user, raw: sentence, title: sentence[0..50].strip, category: category.name)
|
||||
|
||||
puts "creating random reply"
|
||||
PostCreator.create(user, raw: sentence, topic_id: last_topics.sample)
|
||||
|
||||
sleep 3
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe UserTrackingState do
|
||||
describe TopicTrackingState do
|
||||
|
||||
let(:user) do
|
||||
Fabricate(:user)
|
||||
|
@ -10,13 +10,18 @@ describe UserTrackingState do
|
|||
Fabricate(:post)
|
||||
end
|
||||
|
||||
it "can correctly publish unread" do
|
||||
# TODO setup stuff and look at messages
|
||||
TopicTrackingState.publish_unread(post)
|
||||
end
|
||||
|
||||
it "correctly gets the tracking state" do
|
||||
report = UserTrackingState.report([user.id])
|
||||
report = TopicTrackingState.report([user.id])
|
||||
report.length.should == 0
|
||||
|
||||
new_post = post
|
||||
|
||||
report = UserTrackingState.report([user.id])
|
||||
report = TopicTrackingState.report([user.id])
|
||||
|
||||
report.length.should == 1
|
||||
row = report[0]
|
||||
|
@ -27,15 +32,15 @@ describe UserTrackingState do
|
|||
row.user_id.should == user.id
|
||||
|
||||
# lets not leak out random users
|
||||
UserTrackingState.report([post.user_id]).should be_empty
|
||||
TopicTrackingState.report([post.user_id]).should be_empty
|
||||
|
||||
# lets not return anything if we scope on non-existing topic
|
||||
UserTrackingState.report([user.id], post.topic_id + 1).should be_empty
|
||||
TopicTrackingState.report([user.id], post.topic_id + 1).should be_empty
|
||||
|
||||
# when we reply the poster should have an unread row
|
||||
Fabricate(:post, user: user, topic: post.topic)
|
||||
|
||||
report = UserTrackingState.report([post.user_id, user.id])
|
||||
report = TopicTrackingState.report([post.user_id, user.id])
|
||||
report.length.should == 1
|
||||
|
||||
row = report[0]
|
||||
|
@ -51,6 +56,6 @@ describe UserTrackingState do
|
|||
post.topic.category_id = category.id
|
||||
post.topic.save
|
||||
|
||||
UserTrackingState.report([post.user_id, user.id]).count.should == 0
|
||||
TopicTrackingState.report([post.user_id, user.id]).count.should == 0
|
||||
end
|
||||
end
|
|
@ -216,6 +216,14 @@ describe TopicUser do
|
|||
|
||||
end
|
||||
|
||||
it "can scope by tracking" do
|
||||
TopicUser.create!(user_id: 1, topic_id: 1, notification_level: TopicUser.notification_levels[:tracking])
|
||||
TopicUser.create!(user_id: 2, topic_id: 1, notification_level: TopicUser.notification_levels[:watching])
|
||||
TopicUser.create!(user_id: 3, topic_id: 1, notification_level: TopicUser.notification_levels[:regular])
|
||||
|
||||
TopicUser.tracking(1).count.should == 2
|
||||
TopicUser.tracking(10).count.should == 0
|
||||
end
|
||||
|
||||
it "is able to self heal" do
|
||||
p1 = Fabricate(:post)
|
||||
|
|
Loading…
Reference in New Issue
Block a user