FEATURE: add composer warning when user haven't been seen in a long time (#18340)

* FEATURE: add composer warning when user haven't been seen in a long time

When a user creates a PM and adds a recipient that hasn't been seen in a
long time then we'll now show a warning in composer indicating that the
user hasn't been seen in a long time.
This commit is contained in:
Arpit Jalan 2022-09-27 22:06:40 +05:30 committed by GitHub
parent 0f5db0838d
commit 2ee721f8aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 183 additions and 0 deletions

View File

@ -5,8 +5,10 @@ import LinkLookup from "discourse/lib/link-lookup";
import { not } from "@ember/object/computed";
import { scheduleOnce } from "@ember/runloop";
import showModal from "discourse/lib/show-modal";
import { ajax } from "discourse/lib/ajax";
let _messagesCache = {};
let _recipient_names = [];
export default Component.extend({
classNameBindings: [":composer-popup-container", "hidden"],
@ -18,6 +20,7 @@ export default Component.extend({
_similarTopicsMessage: null,
_yourselfConfirm: null,
similarTopics: null,
usersNotSeen: null,
hidden: not("composer.viewOpenOrFullscreen"),
@ -119,6 +122,53 @@ export default Component.extend({
const composer = this.composer;
if (composer.get("privateMessage")) {
const recipients = composer.targetRecipientsArray;
const recipient_names = recipients
.filter((r) => r.type === "user")
.map(({ name }) => name);
if (
recipient_names.length > 0 &&
recipient_names.length !== _recipient_names.length &&
!recipient_names.every((v, i) => v === _recipient_names[i])
) {
_recipient_names = recipient_names;
ajax(`/composer_messages/user_not_seen_in_a_while`, {
type: "GET",
data: {
usernames: recipient_names,
},
}).then((response) => {
if (
response.user_count > 0 &&
this.get("usersNotSeen") !== response.usernames.join("-")
) {
this.set("usersNotSeen", response.usernames.join("-"));
this.messagesByTemplate["education"] = undefined;
let usernames = [];
response.usernames.forEach((username, index) => {
usernames[
index
] = `<a class='mention' href='/u/${username}'>@${username}</a>`;
});
let body_key = "composer.user_not_seen_in_a_while.single";
if (response.user_count > 1) {
body_key = "composer.user_not_seen_in_a_while.multiple";
}
const message = composer.store.createRecord("composer-message", {
id: "user-not-seen",
templateName: "education",
body: I18n.t(body_key, {
usernames: usernames.join(", "),
time_ago: response.time_ago,
}),
});
this.send("popup", message);
}
});
}
if (
recipients.length > 0 &&

View File

@ -0,0 +1,41 @@
import {
acceptance,
exists,
query,
} from "discourse/tests/helpers/qunit-helpers";
import { click, triggerKeyEvent, visit } from "@ember/test-helpers";
import { test } from "qunit";
import I18n from "I18n";
acceptance("Composer - Messages", function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.get("/composer_messages/user_not_seen_in_a_while", () => {
return helper.response({
user_count: 1,
usernames: ["charlie"],
time_ago: "1 year ago",
});
});
});
test("Shows warning in composer if user hasn't been seen in a long time.", async function (assert) {
await visit("/u/charlie");
await click("button.compose-pm");
assert.ok(
!exists(".composer-popup"),
"composer warning is not shown by default"
);
await triggerKeyEvent(".d-editor-input", "keyup", "Space");
assert.ok(exists(".composer-popup"), "shows composer warning message");
assert.ok(
query(".composer-popup").innerHTML.includes(
I18n.t("composer.user_not_seen_in_a_while.single", {
usernames: ['<a class="mention" href="/u/charlie">@charlie</a>'],
time_ago: "1 year ago",
})
),
"warning message has correct body"
);
});
});

View File

@ -17,4 +17,22 @@ class ComposerMessagesController < ApplicationController
render_json_dump(json, rest_serializer: true)
end
def user_not_seen_in_a_while
usernames = params.require(:usernames)
users = ComposerMessagesFinder.user_not_seen_in_a_while(usernames)
user_count = users.count
warning_message = nil
if user_count > 0
message_locale = if user_count == 1
"education.user_not_seen_in_a_while.single"
else
"education.user_not_seen_in_a_while.multiple"
end
end
json = { user_count: user_count, usernames: users, time_ago: FreedomPatches::Rails4.time_ago_in_words(SiteSetting.pm_warn_user_last_seen_months_ago.month.ago, true, scope: :'datetime.distance_in_words_verbose') }
render_json_dump(json)
end
end

View File

@ -2275,6 +2275,9 @@ en:
body: "Right now this message is only being sent to yourself!"
slow_mode:
error: "This topic is in slow mode. You already posted recently; you can post again in %{timeLeft}."
user_not_seen_in_a_while:
single: "The person you are messaging, <b>%{usernames}</b>, hasnt been seen here in a very long time %{time_ago}. They may not receive your message. You may wish to seek out alternate methods of contacting %{usernames}."
multiple: "The following people you are messaging: <b>%{usernames}</b>, havent been seen here in a very long time %{time_ago}. They may not receive your message. You may wish to seek out alternate methods of contacting them."
admin_options_title: "Optional staff settings for this topic"

View File

@ -2149,6 +2149,8 @@ en:
disable_avatar_education_message: "Disable education message for changing avatar."
pm_warn_user_last_seen_months_ago: "When creating a new PM warn users when target recepient has not been seen more than n months ago."
suppress_uncategorized_badge: "Don't show the badge for uncategorized topics in topic lists."
header_dropdown_category_count: "How many categories can be displayed in the header dropdown menu."

View File

@ -387,6 +387,7 @@ Discourse::Application.routes.draw do
end
get "session/scopes" => "session#scopes"
get "composer_messages" => "composer_messages#index"
get "composer_messages/user_not_seen_in_a_while" => "composer_messages#user_not_seen_in_a_while"
resources :static
post "login" => "static#enter"

View File

@ -2272,6 +2272,7 @@ uncategorized:
get_a_room_threshold: 3
dominating_topic_minimum_percent: 20
disable_avatar_education_message: false
pm_warn_user_last_seen_months_ago: 24
global_notice:
default: ""

View File

@ -212,6 +212,10 @@ class ComposerMessagesFinder
}
end
def self.user_not_seen_in_a_while(usernames)
User.where(username_lower: usernames).where("last_seen_at < ?", SiteSetting.pm_warn_user_last_seen_months_ago.months.ago).pluck(:username).sort
end
private
def educate_reply?(type)

View File

@ -501,4 +501,26 @@ RSpec.describe ComposerMessagesFinder do
expect(edit_post_finder.find).to eq(nil)
end
end
describe '#user_not_seen_in_a_while' do
fab!(:user_1) { Fabricate(:user, last_seen_at: 3.years.ago) }
fab!(:user_2) { Fabricate(:user, last_seen_at: 2.years.ago) }
fab!(:user_3) { Fabricate(:user, last_seen_at: 6.months.ago) }
before do
SiteSetting.pm_warn_user_last_seen_months_ago = 24
end
it 'returns users that have not been seen recently' do
users = ComposerMessagesFinder.user_not_seen_in_a_while([user_1.username, user_2.username, user_3.username])
expect(users).to contain_exactly(user_1.username, user_2.username)
end
it 'accounts for pm_warn_user_last_seen_months_ago site setting' do
SiteSetting.pm_warn_user_last_seen_months_ago = 30
users = ComposerMessagesFinder.user_not_seen_in_a_while([user_1.username, user_2.username, user_3.username])
expect(users).to contain_exactly(user_1.username)
end
end
end

View File

@ -30,4 +30,45 @@ RSpec.describe ComposerMessagesController do
end
end
end
describe '#user_not_seen_in_a_while' do
fab!(:user_1) { Fabricate(:user, last_seen_at: 3.years.ago) }
fab!(:user_2) { Fabricate(:user, last_seen_at: 2.years.ago) }
fab!(:user_3) { Fabricate(:user, last_seen_at: 6.months.ago) }
it 'requires you to be logged in' do
get '/composer_messages/user_not_seen_in_a_while.json', params: { usernames: [user_1.username, user_2.username, user_3.username] }
expect(response.status).to eq(403)
end
context 'when logged in' do
let!(:user) { sign_in(Fabricate(:user)) }
before do
SiteSetting.pm_warn_user_last_seen_months_ago = 24
end
it 'requires usernames parameter to be present' do
get '/composer_messages/user_not_seen_in_a_while.json'
expect(response.status).to eq(400)
end
it 'returns users that have not been seen recently' do
get '/composer_messages/user_not_seen_in_a_while.json', params: { usernames: [user_1.username, user_2.username, user_3.username] }
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["user_count"]).to eq(2)
expect(json["usernames"]).to contain_exactly(user_1.username, user_2.username)
end
it 'accounts for pm_warn_user_last_seen_months_ago site setting' do
SiteSetting.pm_warn_user_last_seen_months_ago = 30
get '/composer_messages/user_not_seen_in_a_while.json', params: { usernames: [user_1.username, user_2.username, user_3.username] }
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["user_count"]).to eq(1)
expect(json["usernames"]).to contain_exactly(user_1.username)
end
end
end
end