FEATURE: Special call-out for new / returning posters. (#7115)
diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6
index b63c224..e0d0866 100644
--- a/app/assets/javascripts/discourse/lib/transform-post.js.es6
+++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6
@@ -134,6 +134,13 @@ export default function transformPost(
postAtts.topicUrl = topic.get("url");
postAtts.isSaving = post.isSaving;
+ if (post.post_notice_type) {
+ postAtts.postNoticeType = post.post_notice_type;
+ if (postAtts.postNoticeType === "returning") {
+ postAtts.postNoticeTime = new Date(post.post_notice_time);
+ }
+ }
+
const showPMMap =
topic.archetype === "private_message" && post.post_number === 1;
if (showPMMap) {
diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6
index fbbda45..956dafd 100644
--- a/app/assets/javascripts/discourse/widgets/post.js.es6
+++ b/app/assets/javascripts/discourse/widgets/post.js.es6
@@ -13,6 +13,7 @@ import {
formatUsername
} from "discourse/lib/utilities";
import hbs from "discourse/widgets/hbs-compiler";
+import { relativeAge } from "discourse/lib/formatter";
function transformWithCallbacks(post) {
let transformed = transformBasicPost(post);
@@ -427,6 +428,29 @@ createWidget("post-contents", {
}
});
+createWidget("post-notice", {
+ tagName: "div.post-notice",
+
+ html(attrs) {
+ let text, icon;
+ if (attrs.postNoticeType === "first") {
+ icon = "hands-helping";
+ text = I18n.t("post.notice.first", { user: attrs.username });
+ } else if (attrs.postNoticeType === "returning") {
+ icon = "far-smile";
+ text = I18n.t("post.notice.return", {
+ user: attrs.username,
+ time: relativeAge(attrs.postNoticeTime, {
+ format: "tiny",
+ addAgo: true
+ })
+ });
+ }
+
+ return h("p", [iconNode(icon), text]);
+ }
+});
+
createWidget("post-body", {
tagName: "div.topic-body.clearfix",
@@ -505,6 +529,10 @@ createWidget("post-article", {
);
}
+ if (attrs.postNoticeType) {
+ rows.push(h("div.row", [this.attach("post-notice", attrs)]));
+ }
+
rows.push(
h("div.row", [
this.attach("post-avatar", attrs),
diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss
index 09247ad..8f5f630 100644
--- a/app/assets/stylesheets/common/base/topic-post.scss
+++ b/app/assets/stylesheets/common/base/topic-post.scss
@@ -864,3 +864,22 @@ a.mention-group {
margin-bottom: 1em;
}
}
+
+.post-notice {
+ background-color: $tertiary-low;
+ border-top: 1px solid $primary-low;
+ color: $primary;
+ padding: 1em;
+ width: calc(
+ #{$topic-body-width} + #{$topic-avatar-width} - #{$topic-body-width-padding} +
+ 3px
+ );
+
+ p {
+ margin: 0;
+ }
+
+ .d-icon {
+ margin-right: 1em;
+ }
+}
diff --git a/app/models/post.rb b/app/models/post.rb
index 00a4db5..73f52ec 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -194,6 +194,7 @@ class Post < ActiveRecord::Base
def recover!
super
update_flagged_posts_count
+ delete_post_notices
recover_public_post_actions
TopicLink.extract_from(self)
QuotedPost.extract_from(self)
@@ -381,6 +382,11 @@ class Post < ActiveRecord::Base
PostAction.update_flagged_posts_count
end
+ def delete_post_notices
+ self.custom_fields.delete("post_notice_type")
+ self.custom_fields.delete("post_notice_time")
+ end
+
def recover_public_post_actions
PostAction.publics
.with_deleted
diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb
index 42686fa..916e2cd 100644
--- a/app/serializers/post_serializer.rb
+++ b/app/serializers/post_serializer.rb
@@ -70,6 +70,8 @@ class PostSerializer < BasicPostSerializer
:is_auto_generated,
:action_code,
:action_code_who,
+ :post_notice_type,
+ :post_notice_time,
:last_wiki_edit,
:locked,
:excerpt
@@ -363,6 +365,22 @@ class PostSerializer < BasicPostSerializer
include_action_code? && action_code_who.present?
end
+ def post_notice_type
+ post_custom_fields["post_notice_type"]
+ end
+
+ def include_post_notice_type?
+ post_notice_type.present?
+ end
+
+ def post_notice_time
+ post_custom_fields["post_notice_time"]
+ end
+
+ def include_post_notice_time?
+ post_notice_time.present?
+ end
+
def locked
true
end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 6141724..bb1eb39 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -2149,6 +2149,10 @@ en:
one: "view 1 hidden reply"
other: "view {{count}} hidden replies"
+ notice:
+ first: "This is the first time {{user}} has posted — let's welcome them to our community!"
+ return: "It's been a while since we've seen {{user}} — their last post was in {{time}}."
+
unread: "Post is unread"
has_replies:
one: "{{count}} Reply"
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index f457a08..300697c 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1901,6 +1901,8 @@ en:
max_allowed_message_recipients: "Maximum recipients allowed in a message."
watched_words_regular_expressions: "Watched words are regular expressions."
+ returning_users_days: "How many days should pass before a user is considered to be returning."
+
default_email_digest_frequency: "How often users receive summary emails by default."
default_include_tl0_in_digests: "Include posts from new users in summary emails by default. Users can change this in their preferences."
default_email_personal_messages: "Send an email when someone messages the user by default."
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 627f4d5..8bd2244 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -807,6 +807,8 @@ posting:
default: false
client: true
shadowed_by_global: true
+ returning_users_days:
+ default: 60
email:
email_time_window_mins:
diff --git a/lib/post_creator.rb b/lib/post_creator.rb
index d8bee40..6f77319 100644
--- a/lib/post_creator.rb
+++ b/lib/post_creator.rb
@@ -165,6 +165,7 @@ class PostCreator
transaction do
build_post_stats
create_topic
+ create_post_notice
save_post
extract_links
track_topic
@@ -508,6 +509,21 @@ class PostCreator
@user.update_attributes(last_posted_at: @post.created_at)
end
+ def create_post_notice
+ last_post_time = Post.where(user_id: @user.id)
+ .order(created_at: :desc)
+ .limit(1)
+ .pluck(:created_at)
+ .first
+
+ if !last_post_time
+ @post.custom_fields["post_notice_type"] = "first"
+ elsif SiteSetting.returning_users_days > 0 && last_post_time < SiteSetting.returning_users_days.days.ago
+ @post.custom_fields["post_notice_type"] = "returning"
+ @post.custom_fields["post_notice_time"] = last_post_time
+ end
+ end
+
def publish
return if @opts[:import_mode] || @post.post_number == 1
@post.publish_change_to_clients! :created
diff --git a/lib/svg_sprite/svg_sprite.rb b/lib/svg_sprite/svg_sprite.rb
index 7037c7a..f6946b2 100644
--- a/lib/svg_sprite/svg_sprite.rb
+++ b/lib/svg_sprite/svg_sprite.rb
@@ -118,6 +118,7 @@ module SvgSprite
"globe",
"globe-americas",
"hand-point-right",
+ "hands-helping",
"heading",
"heart",
"home",
diff --git a/lib/topic_view.rb b/lib/topic_view.rb
index c40f82f..4f8c0ac 100644
--- a/lib/topic_view.rb
+++ b/lib/topic_view.rb
@@ -18,7 +18,7 @@ class TopicView
end
def self.default_post_custom_fields
- @default_post_custom_fields ||= ["action_code_who"]
[... diff too long, it was truncated ...]
GitHub sha: 35942f7c