mirror of
https://github.com/discourse/discourse.git
synced 2025-02-17 04:53:16 +08:00
FEATURE: user status (#16875)
This commit is contained in:
parent
ac59168dde
commit
5c596273a0
|
@ -238,6 +238,8 @@ const SiteHeaderComponent = MountWidget.extend(
|
||||||
|
|
||||||
this.appEvents.on("dom:clean", this, "_cleanDom");
|
this.appEvents.on("dom:clean", this, "_cleanDom");
|
||||||
|
|
||||||
|
this.appEvents.on("user-status:changed", () => this.queueRerender());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.currentUser &&
|
this.currentUser &&
|
||||||
!this.get("currentUser.read_first_notification")
|
!this.get("currentUser.read_first_notification")
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { getURLWithCDN } from "discourse-common/lib/get-url";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
|
import { emojiUnescape } from "discourse/lib/text";
|
||||||
|
|
||||||
export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
||||||
elementId: "user-card",
|
elementId: "user-card",
|
||||||
|
@ -49,6 +50,17 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, {
|
||||||
return user.location || user.website_name || this.userTimezone;
|
return user.location || user.website_name || this.userTimezone;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("user.status")
|
||||||
|
hasStatus() {
|
||||||
|
return this.siteSettings.enable_user_status && this.user.status;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("user.status")
|
||||||
|
userStatusEmoji() {
|
||||||
|
const emoji = this.user.status.emoji ?? "mega";
|
||||||
|
return emojiUnescape(`:${emoji}:`);
|
||||||
|
},
|
||||||
|
|
||||||
isSuspendedOrHasBio: or("user.suspend_reason", "user.bio_excerpt"),
|
isSuspendedOrHasBio: or("user.suspend_reason", "user.bio_excerpt"),
|
||||||
showCheckEmail: and("user.staged", "canCheckEmails"),
|
showCheckEmail: and("user.staged", "canCheckEmails"),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import Controller from "@ember/controller";
|
||||||
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { notEmpty } from "@ember/object/computed";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import bootbox from "bootbox";
|
||||||
|
|
||||||
|
export default Controller.extend(ModalFunctionality, {
|
||||||
|
userStatusService: service("user-status"),
|
||||||
|
|
||||||
|
description: null,
|
||||||
|
statusIsSet: notEmpty("description"),
|
||||||
|
showDeleteButton: false,
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
if (this.currentUser.status) {
|
||||||
|
this.setProperties({
|
||||||
|
description: this.currentUser.status.description,
|
||||||
|
showDeleteButton: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
delete() {
|
||||||
|
this.userStatusService
|
||||||
|
.clear()
|
||||||
|
.then(() => {
|
||||||
|
this._resetModal();
|
||||||
|
this.send("closeModal");
|
||||||
|
})
|
||||||
|
.catch((e) => this._handleError(e));
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
saveAndClose() {
|
||||||
|
if (this.description) {
|
||||||
|
const status = { description: this.description };
|
||||||
|
this.userStatusService
|
||||||
|
.set(status)
|
||||||
|
.then(() => {
|
||||||
|
this.send("closeModal");
|
||||||
|
})
|
||||||
|
.catch((e) => this._handleError(e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleError(e) {
|
||||||
|
if (typeof e === "string") {
|
||||||
|
bootbox.alert(e);
|
||||||
|
} else {
|
||||||
|
popupAjaxError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_resetModal() {
|
||||||
|
this.set("description", null);
|
||||||
|
this.set("showDeleteButton", false);
|
||||||
|
},
|
||||||
|
});
|
|
@ -252,6 +252,13 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
||||||
hasGroups,
|
hasGroups,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setUserStatus() {
|
||||||
|
showModal("user-status", {
|
||||||
|
title: "user_status.set_custom_status",
|
||||||
|
modalClass: "user-status",
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
renderTemplate() {
|
renderTemplate() {
|
||||||
|
|
27
app/assets/javascripts/discourse/app/services/user-status.js
Normal file
27
app/assets/javascripts/discourse/app/services/user-status.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import Service, { inject as service } from "@ember/service";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
|
export default class UserStatusService extends Service {
|
||||||
|
@service appEvents;
|
||||||
|
|
||||||
|
async set(status) {
|
||||||
|
await ajax({
|
||||||
|
url: "/user-status.json",
|
||||||
|
type: "PUT",
|
||||||
|
data: { description: status.description },
|
||||||
|
});
|
||||||
|
|
||||||
|
this.currentUser.set("status", status);
|
||||||
|
this.appEvents.trigger("do-not-disturb:changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
await ajax({
|
||||||
|
url: "/user-status.json",
|
||||||
|
type: "DELETE",
|
||||||
|
});
|
||||||
|
|
||||||
|
this.currentUser.set("status", null);
|
||||||
|
this.appEvents.trigger("do-not-disturb:changed");
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
toggleAnonymous=(route-action "toggleAnonymous")
|
toggleAnonymous=(route-action "toggleAnonymous")
|
||||||
logout=(route-action "logout")
|
logout=(route-action "logout")
|
||||||
toggleSidebar=(route-action "toggleSidebar")
|
toggleSidebar=(route-action "toggleSidebar")
|
||||||
|
setUserStatus=(route-action "setUserStatus")
|
||||||
}}
|
}}
|
||||||
{{software-update-prompt}}
|
{{software-update-prompt}}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,9 @@
|
||||||
{{#if this.user.staged}}
|
{{#if this.user.staged}}
|
||||||
<h2 class="staged">{{i18n "user.staged"}}</h2>
|
<h2 class="staged">{{i18n "user.staged"}}</h2>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if this.hasStatus}}
|
||||||
|
<h3 class="user-status">{{html-safe this.userStatusEmoji}} {{this.user.status.description}}</h3>
|
||||||
|
{{/if}}
|
||||||
{{plugin-outlet name="user-card-post-names" connectorTagName="div" args=(hash user=this.user) tagName="div"}}
|
{{plugin-outlet name="user-card-post-names" connectorTagName="div" args=(hash user=this.user) tagName="div"}}
|
||||||
</div>
|
</div>
|
||||||
<ul class="usercard-controls">
|
<ul class="usercard-controls">
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{{#d-modal-body}}
|
||||||
|
{{#conditional-loading-spinner condition=loading}}
|
||||||
|
<div class="control-group user-status-description-wrap">
|
||||||
|
{{input
|
||||||
|
class="user-status-description"
|
||||||
|
placeholder=(i18n "user_status.what_are_you_doing")
|
||||||
|
value=description}}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer control-group">
|
||||||
|
{{d-button
|
||||||
|
label="user_status.save"
|
||||||
|
class="btn-primary"
|
||||||
|
disabled=(not statusIsSet)
|
||||||
|
action=(action "saveAndClose")}}
|
||||||
|
{{d-modal-cancel close=(action "closeModal")}}
|
||||||
|
{{#if showDeleteButton}}
|
||||||
|
{{d-button
|
||||||
|
icon="trash-alt"
|
||||||
|
class="delete-status btn-danger"
|
||||||
|
action=(action "delete")}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/conditional-loading-spinner}}
|
||||||
|
{{/d-modal-body}}
|
|
@ -86,9 +86,13 @@ export const ButtonClass = {
|
||||||
html(attrs) {
|
html(attrs) {
|
||||||
const contents = [];
|
const contents = [];
|
||||||
const left = !attrs.iconRight;
|
const left = !attrs.iconRight;
|
||||||
|
|
||||||
if (attrs.icon && left) {
|
if (attrs.icon && left) {
|
||||||
contents.push(this._buildIcon(attrs));
|
contents.push(this._buildIcon(attrs));
|
||||||
}
|
}
|
||||||
|
if (attrs.emoji && left) {
|
||||||
|
contents.push(this.attach("emoji", { name: attrs.emoji }));
|
||||||
|
}
|
||||||
if (attrs.label) {
|
if (attrs.label) {
|
||||||
contents.push(
|
contents.push(
|
||||||
h("span.d-button-label", I18n.t(attrs.label, attrs.labelOptions))
|
h("span.d-button-label", I18n.t(attrs.label, attrs.labelOptions))
|
||||||
|
@ -106,6 +110,9 @@ export const ButtonClass = {
|
||||||
if (attrs.contents) {
|
if (attrs.contents) {
|
||||||
contents.push(attrs.contents);
|
contents.push(attrs.contents);
|
||||||
}
|
}
|
||||||
|
if (attrs.emoji && !left) {
|
||||||
|
contents.push(this.attach("emoji", { name: attrs.emoji }));
|
||||||
|
}
|
||||||
if (attrs.icon && !left) {
|
if (attrs.icon && !left) {
|
||||||
contents.push(this._buildIcon(attrs));
|
contents.push(this._buildIcon(attrs));
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,14 @@ createWidget("header-notifications", {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (this.currentUser.status) {
|
||||||
|
contents.push(
|
||||||
|
this.attach("user-status-bubble", {
|
||||||
|
emoji: this.currentUser.status.emoji,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (user.isInDoNotDisturb()) {
|
if (user.isInDoNotDisturb()) {
|
||||||
contents.push(h("div.do-not-disturb-background", iconNode("moon")));
|
contents.push(h("div.do-not-disturb-background", iconNode("moon")));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,13 +16,40 @@ createWidgetFrom(QuickAccessItem, "logout-item", {
|
||||||
html() {
|
html() {
|
||||||
return this.attach("flat-button", {
|
return this.attach("flat-button", {
|
||||||
action: "logout",
|
action: "logout",
|
||||||
content: I18n.t("user.log_out"),
|
|
||||||
icon: "sign-out-alt",
|
icon: "sign-out-alt",
|
||||||
label: "user.log_out",
|
label: "user.log_out",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createWidgetFrom(QuickAccessItem, "user-status-item", {
|
||||||
|
tagName: "li.user-status",
|
||||||
|
|
||||||
|
html() {
|
||||||
|
const action = "hideMenuAndSetStatus";
|
||||||
|
const userStatus = this.currentUser.status;
|
||||||
|
if (userStatus) {
|
||||||
|
const emoji = userStatus.emoji ?? "mega";
|
||||||
|
return this.attach("flat-button", {
|
||||||
|
action,
|
||||||
|
emoji,
|
||||||
|
translatedLabel: userStatus.description,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.attach("flat-button", {
|
||||||
|
action,
|
||||||
|
icon: "plus-circle",
|
||||||
|
label: "user_status.set_custom_status",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hideMenuAndSetStatus() {
|
||||||
|
this.sendWidgetAction("toggleUserMenu");
|
||||||
|
this.sendWidgetAction("setUserStatus");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
createWidgetFrom(QuickAccessPanel, "quick-access-profile", {
|
createWidgetFrom(QuickAccessPanel, "quick-access-profile", {
|
||||||
tagName: "div.quick-access-panel.quick-access-profile",
|
tagName: "div.quick-access-panel.quick-access-profile",
|
||||||
|
|
||||||
|
@ -43,11 +70,16 @@ createWidgetFrom(QuickAccessPanel, "quick-access-profile", {
|
||||||
},
|
},
|
||||||
|
|
||||||
_getItems() {
|
_getItems() {
|
||||||
let items = this._getDefaultItems();
|
const items = [];
|
||||||
|
|
||||||
|
if (this.siteSettings.enable_user_status) {
|
||||||
|
items.push({ widget: "user-status-item" });
|
||||||
|
}
|
||||||
|
items.push(...this._getDefaultItems());
|
||||||
if (this._showToggleAnonymousButton()) {
|
if (this._showToggleAnonymousButton()) {
|
||||||
items.push(this._toggleAnonymousButton());
|
items.push(this._toggleAnonymousButton());
|
||||||
}
|
}
|
||||||
items = items.concat(_extraItems);
|
items.push(..._extraItems);
|
||||||
|
|
||||||
if (this.attrs.showLogoutButton) {
|
if (this.attrs.showLogoutButton) {
|
||||||
items.push({ widget: "logout-item" });
|
items.push({ widget: "logout-item" });
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { createWidget } from "discourse/widgets/widget";
|
||||||
|
|
||||||
|
export default createWidget("user-status-bubble", {
|
||||||
|
tagName: "div.user-status-background",
|
||||||
|
|
||||||
|
html(attrs) {
|
||||||
|
const emoji = attrs.emoji ?? "mega";
|
||||||
|
return this.attach("emoji", { name: emoji });
|
||||||
|
},
|
||||||
|
});
|
|
@ -76,3 +76,30 @@ acceptance(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
acceptance("User Card - User Status", function (needs) {
|
||||||
|
needs.user();
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
const response = cloneJSON(userFixtures["/u/charlie/card.json"]);
|
||||||
|
response.user.status = { description: "off to dentist" };
|
||||||
|
server.get("/u/charlie/card.json", () => helper.response(response));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows user status if enabled", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click('a[data-user-card="charlie"]');
|
||||||
|
|
||||||
|
assert.ok(exists(".user-card h3.user-status"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("doesn't show user status if disabled", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = false;
|
||||||
|
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click('a[data-user-card="charlie"]');
|
||||||
|
|
||||||
|
assert.notOk(exists(".user-card h3.user-status"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
import {
|
||||||
|
acceptance,
|
||||||
|
exists,
|
||||||
|
query,
|
||||||
|
updateCurrentUser,
|
||||||
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||||
|
import { test } from "qunit";
|
||||||
|
|
||||||
|
acceptance("User Status", function (needs) {
|
||||||
|
needs.user();
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.put("/user-status.json", () => helper.response({ success: true }));
|
||||||
|
server.delete("/user-status.json", () =>
|
||||||
|
helper.response({ success: true })
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const userStatusFallbackEmoji = "mega";
|
||||||
|
const userStatus = "off to dentist";
|
||||||
|
|
||||||
|
test("doesn't show the user status button on the menu by default", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = false;
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
|
||||||
|
assert.notOk(exists("div.quick-access-panel li.user-status"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows the user status button on the menu when disabled in settings", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
exists("div.quick-access-panel li.user-status"),
|
||||||
|
"shows the button"
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
exists("div.quick-access-panel li.user-status svg.d-icon-plus-circle"),
|
||||||
|
"shows the icon on the button"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows user status on loaded page", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
updateCurrentUser({ status: { description: userStatus } });
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
query("div.quick-access-panel li.user-status span.d-button-label")
|
||||||
|
.innerText,
|
||||||
|
userStatus,
|
||||||
|
"shows user status description on the menu"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
query("div.quick-access-panel li.user-status img.emoji").alt,
|
||||||
|
`:${userStatusFallbackEmoji}:`,
|
||||||
|
"shows user status emoji on the menu"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
query(".header-dropdown-toggle .user-status-background img.emoji").alt,
|
||||||
|
`:${userStatusFallbackEmoji}:`,
|
||||||
|
"shows user status emoji on the user avatar in the header"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("setting user status", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
await click(".user-status button");
|
||||||
|
await fillIn(".user-status-description", userStatus);
|
||||||
|
await click(".btn-primary");
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
query(".header-dropdown-toggle .user-status-background img.emoji").alt,
|
||||||
|
`:${userStatusFallbackEmoji}:`,
|
||||||
|
"shows user status emoji on the user avatar in the header"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
assert.equal(
|
||||||
|
query("div.quick-access-panel li.user-status span.d-button-label")
|
||||||
|
.innerText,
|
||||||
|
userStatus,
|
||||||
|
"shows user status description on the menu"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
query("div.quick-access-panel li.user-status img.emoji").alt,
|
||||||
|
`:${userStatusFallbackEmoji}:`,
|
||||||
|
"shows user status emoji on the menu"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("updating user status", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
updateCurrentUser({ status: { description: userStatus } });
|
||||||
|
const updatedStatus = "off to dentist the second time";
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
await click(".user-status button");
|
||||||
|
await fillIn(".user-status-description", updatedStatus);
|
||||||
|
await click(".btn-primary");
|
||||||
|
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
assert.equal(
|
||||||
|
query("div.quick-access-panel li.user-status span.d-button-label")
|
||||||
|
.innerText,
|
||||||
|
updatedStatus,
|
||||||
|
"shows user status description on the menu"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("clearing user status", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
updateCurrentUser({ status: { description: userStatus } });
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
await click(".user-status button");
|
||||||
|
await click(".btn.delete-status");
|
||||||
|
|
||||||
|
assert.notOk(exists(".header-dropdown-toggle .user-status-background"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shows the trash button when editing status that was set before", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
updateCurrentUser({ status: { description: userStatus } });
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
await click(".user-status button");
|
||||||
|
|
||||||
|
assert.ok(exists(".btn.delete-status"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("doesn't show the trash button when status wasn't set before", async function (assert) {
|
||||||
|
this.siteSettings.enable_user_status = true;
|
||||||
|
updateCurrentUser({ status: null });
|
||||||
|
|
||||||
|
await visit("/");
|
||||||
|
await click(".header-dropdown-toggle.current-user");
|
||||||
|
await click(".menu-links-row .user-preferences-link");
|
||||||
|
await click(".user-status button");
|
||||||
|
|
||||||
|
assert.notOk(exists(".btn.delete-status"));
|
||||||
|
});
|
||||||
|
});
|
|
@ -41,6 +41,20 @@ discourseModule("Integration | Component | Widget | button", function (hooks) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
componentTest("emoji and text button", {
|
||||||
|
template: hbs`{{mount-widget widget="button" args=args}}`,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("args", { emoji: "mega", label: "topic.create" });
|
||||||
|
},
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.ok(exists("button.widget-button"), "renders the widget");
|
||||||
|
assert.ok(exists("button img.emoji"), "it renders the emoji");
|
||||||
|
assert.ok(exists("button span.d-button-label"), "it renders the label");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
componentTest("text only button", {
|
componentTest("text only button", {
|
||||||
template: hbs`{{mount-widget widget="button" args=args}}`,
|
template: hbs`{{mount-widget widget="button" args=args}}`,
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
@import "common/foundation/base";
|
@import "common/foundation/base";
|
||||||
@import "common/select-kit/_index";
|
@import "common/select-kit/_index";
|
||||||
@import "common/components/_index";
|
@import "common/components/_index";
|
||||||
|
@import "common/modal/_index";
|
||||||
@import "common/input_tip";
|
@import "common/input_tip";
|
||||||
@import "common/topic-entrance";
|
@import "common/topic-entrance";
|
||||||
@import "common/printer-friendly";
|
@import "common/printer-friendly";
|
||||||
|
|
|
@ -401,7 +401,7 @@ table {
|
||||||
|
|
||||||
.d-header .header-dropdown-toggle .do-not-disturb-background {
|
.d-header .header-dropdown-toggle .do-not-disturb-background {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 2px;
|
left: 0;
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
z-index: 1002;
|
z-index: 1002;
|
||||||
}
|
}
|
||||||
|
@ -424,6 +424,29 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d-header .header-dropdown-toggle .user-status-background {
|
||||||
|
position: absolute;
|
||||||
|
right: -3px;
|
||||||
|
bottom: -1px;
|
||||||
|
z-index: 1002;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-status-background {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
background-color: var(--tertiary-low);
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
.emoji {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.user-menu .quick-access-panel li.do-not-disturb {
|
.user-menu .quick-access-panel li.do-not-disturb {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 0 100%;
|
flex: 0 0 100%;
|
||||||
|
|
|
@ -338,6 +338,13 @@
|
||||||
padding-top: 0.2em;
|
padding-top: 0.2em;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.emoji {
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.is-warning {
|
.is-warning {
|
||||||
.d-icon-far-envelope {
|
.d-icon-far-envelope {
|
||||||
|
|
|
@ -300,3 +300,9 @@ $avatar_margin: -50px; // negative margin makes avatars extend above cards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3.user-status {
|
||||||
|
img.emoji {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
app/assets/stylesheets/common/modal/_index.scss
Normal file
1
app/assets/stylesheets/common/modal/_index.scss
Normal file
|
@ -0,0 +1 @@
|
||||||
|
@import "user-status";
|
38
app/assets/stylesheets/common/modal/user-status.scss
Normal file
38
app/assets/stylesheets/common/modal/user-status.scss
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
.user-status.modal {
|
||||||
|
.modal-inner-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 310px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
margin: 0;
|
||||||
|
border-top: 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.delete-status {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
width: 375px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ember-text-field.user-status-description {
|
||||||
|
min-width: 220px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-status-description-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
app/controllers/user_status_controller.rb
Normal file
25
app/controllers/user_status_controller.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserStatusController < ApplicationController
|
||||||
|
requires_login
|
||||||
|
|
||||||
|
def set
|
||||||
|
ensure_feature_enabled
|
||||||
|
raise Discourse::InvalidParameters.new(:description) if params[:description].blank?
|
||||||
|
|
||||||
|
current_user.set_status!(params[:description])
|
||||||
|
render json: success_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
ensure_feature_enabled
|
||||||
|
current_user.clear_status!
|
||||||
|
render json: success_json
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_feature_enabled
|
||||||
|
raise ActionController::RoutingError.new("Not Found") if !SiteSetting.enable_user_status
|
||||||
|
end
|
||||||
|
end
|
|
@ -119,7 +119,8 @@ class UsersController < ApplicationController
|
||||||
:card_background_upload,
|
:card_background_upload,
|
||||||
:primary_group,
|
:primary_group,
|
||||||
:flair_group,
|
:flair_group,
|
||||||
:primary_email
|
:primary_email,
|
||||||
|
:user_status
|
||||||
)
|
)
|
||||||
|
|
||||||
users = users.filter { |u| guardian.can_see_profile?(u) }
|
users = users.filter { |u| guardian.can_see_profile?(u) }
|
||||||
|
|
|
@ -63,6 +63,7 @@ class User < ActiveRecord::Base
|
||||||
has_many :muted_user_records, class_name: 'MutedUser', dependent: :delete_all
|
has_many :muted_user_records, class_name: 'MutedUser', dependent: :delete_all
|
||||||
has_many :ignored_user_records, class_name: 'IgnoredUser', dependent: :delete_all
|
has_many :ignored_user_records, class_name: 'IgnoredUser', dependent: :delete_all
|
||||||
has_many :do_not_disturb_timings, dependent: :delete_all
|
has_many :do_not_disturb_timings, dependent: :delete_all
|
||||||
|
has_one :user_status, dependent: :destroy
|
||||||
|
|
||||||
# dependent deleting handled via before_destroy (special cases)
|
# dependent deleting handled via before_destroy (special cases)
|
||||||
has_many :user_actions
|
has_many :user_actions
|
||||||
|
@ -1500,6 +1501,23 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clear_status!
|
||||||
|
user_status.destroy! if user_status
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_status!(description)
|
||||||
|
now = Time.zone.now
|
||||||
|
if user_status
|
||||||
|
user_status.update!(description: description, set_at: now)
|
||||||
|
else
|
||||||
|
self.user_status = UserStatus.create!(
|
||||||
|
user_id: id,
|
||||||
|
description: description,
|
||||||
|
set_at: now
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def badge_grant
|
def badge_grant
|
||||||
|
|
25
app/models/user_status.rb
Normal file
25
app/models/user_status.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserStatus < ActiveRecord::Base
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
validate :ends_at_greater_than_set_at,
|
||||||
|
if: Proc.new { |t| t.will_save_change_to_set_at? || t.will_save_change_to_ends_at? }
|
||||||
|
|
||||||
|
def ends_at_greater_than_set_at
|
||||||
|
if ends_at && set_at > ends_at
|
||||||
|
errors.add(:ends_at, I18n.t("user_status.errors.ends_at_should_be_greater_than_set_at"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: user_statuses
|
||||||
|
#
|
||||||
|
# user_id :integer not null, primary key
|
||||||
|
# emoji :string
|
||||||
|
# description :string not null
|
||||||
|
# set_at :datetime not null
|
||||||
|
# ends_at :datetime
|
||||||
|
#
|
|
@ -70,7 +70,8 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||||
:default_calendar,
|
:default_calendar,
|
||||||
:bookmark_auto_delete_preference,
|
:bookmark_auto_delete_preference,
|
||||||
:pending_posts_count,
|
:pending_posts_count,
|
||||||
:experimental_sidebar_enabled
|
:experimental_sidebar_enabled,
|
||||||
|
:status
|
||||||
|
|
||||||
delegate :user_stat, to: :object, private: true
|
delegate :user_stat, to: :object, private: true
|
||||||
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
|
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
|
||||||
|
@ -336,4 +337,12 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||||
def include_experimental_sidebar_enabled?
|
def include_experimental_sidebar_enabled?
|
||||||
SiteSetting.enable_experimental_sidebar
|
SiteSetting.enable_experimental_sidebar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_status?
|
||||||
|
SiteSetting.enable_user_status
|
||||||
|
end
|
||||||
|
|
||||||
|
def status
|
||||||
|
UserStatusSerializer.new(object.user_status, root: false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,7 +66,8 @@ class UserCardSerializer < BasicUserSerializer
|
||||||
:flair_color,
|
:flair_color,
|
||||||
:featured_topic,
|
:featured_topic,
|
||||||
:timezone,
|
:timezone,
|
||||||
:pending_posts_count
|
:pending_posts_count,
|
||||||
|
:status
|
||||||
|
|
||||||
untrusted_attributes :bio_excerpt,
|
untrusted_attributes :bio_excerpt,
|
||||||
:website,
|
:website,
|
||||||
|
@ -222,6 +223,14 @@ class UserCardSerializer < BasicUserSerializer
|
||||||
object.card_background_upload&.url
|
object.card_background_upload&.url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def include_status?
|
||||||
|
SiteSetting.enable_user_status
|
||||||
|
end
|
||||||
|
|
||||||
|
def status
|
||||||
|
UserStatusSerializer.new(user.user_status, root: false)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def custom_field_keys
|
def custom_field_keys
|
||||||
|
|
5
app/serializers/user_status_serializer.rb
Normal file
5
app/serializers/user_status_serializer.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserStatusSerializer < ApplicationSerializer
|
||||||
|
attributes :description, :emoji
|
||||||
|
end
|
|
@ -37,6 +37,7 @@ class WebHookUserSerializer < UserSerializer
|
||||||
user_auth_token_logs
|
user_auth_token_logs
|
||||||
use_logo_small_as_avatar
|
use_logo_small_as_avatar
|
||||||
pending_posts_count
|
pending_posts_count
|
||||||
|
status
|
||||||
}.each do |attr|
|
}.each do |attr|
|
||||||
define_method("include_#{attr}?") do
|
define_method("include_#{attr}?") do
|
||||||
false
|
false
|
||||||
|
|
|
@ -1785,6 +1785,11 @@ en:
|
||||||
private_message: "message"
|
private_message: "message"
|
||||||
the_topic: "the topic"
|
the_topic: "the topic"
|
||||||
|
|
||||||
|
user_status:
|
||||||
|
save: "Save"
|
||||||
|
set_custom_status: "Set custom status"
|
||||||
|
what_are_you_doing: "What are you doing?"
|
||||||
|
|
||||||
loading: "Loading..."
|
loading: "Loading..."
|
||||||
errors:
|
errors:
|
||||||
prev_page: "while trying to load"
|
prev_page: "while trying to load"
|
||||||
|
|
|
@ -5206,3 +5206,7 @@ en:
|
||||||
small_action_post_raw: "Continue discussion at %{new_title}."
|
small_action_post_raw: "Continue discussion at %{new_title}."
|
||||||
|
|
||||||
fallback_username: "user"
|
fallback_username: "user"
|
||||||
|
|
||||||
|
user_status:
|
||||||
|
errors:
|
||||||
|
ends_at_should_be_greater_than_set_at: "ends_at should be greater than set_at"
|
||||||
|
|
|
@ -1020,6 +1020,9 @@ Discourse::Application.routes.draw do
|
||||||
post "/presence/update" => "presence#update"
|
post "/presence/update" => "presence#update"
|
||||||
get "/presence/get" => "presence#get"
|
get "/presence/get" => "presence#get"
|
||||||
|
|
||||||
|
put "user-status" => "user_status#set"
|
||||||
|
delete "user-status" => "user_status#clear"
|
||||||
|
|
||||||
get "*url", to: 'permalinks#show', constraints: PermalinkConstraint.new
|
get "*url", to: 'permalinks#show', constraints: PermalinkConstraint.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -361,6 +361,10 @@ basic:
|
||||||
default: true
|
default: true
|
||||||
sitemap_page_size:
|
sitemap_page_size:
|
||||||
default: 10000
|
default: 10000
|
||||||
|
enable_user_status:
|
||||||
|
hidden: true
|
||||||
|
client: true
|
||||||
|
default: false
|
||||||
|
|
||||||
login:
|
login:
|
||||||
invite_only:
|
invite_only:
|
||||||
|
|
17
db/migrate/20220519190829_create_user_statuses.rb
Normal file
17
db/migrate/20220519190829_create_user_statuses.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateUserStatuses < ActiveRecord::Migration[7.0]
|
||||||
|
def up
|
||||||
|
create_table :user_statuses, id: false do |t|
|
||||||
|
t.integer :user_id, primary_key: true, null: false
|
||||||
|
t.string :emoji, null: true
|
||||||
|
t.string :description, null: false
|
||||||
|
t.datetime :set_at, null: false
|
||||||
|
t.datetime :ends_at, null: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :user_statuses
|
||||||
|
end
|
||||||
|
end
|
9
spec/fabricators/user_status_fabricator.rb
Normal file
9
spec/fabricators/user_status_fabricator.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Fabricator(:user_status) do
|
||||||
|
user
|
||||||
|
set_at { Time.zone.now }
|
||||||
|
|
||||||
|
description { "off to dentists" }
|
||||||
|
emoji { "tooth" }
|
||||||
|
end
|
14
spec/models/user_status_spec.rb
Normal file
14
spec/models/user_status_spec.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe UserStatus do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
describe "validations" do
|
||||||
|
it 'is invalid when ends_at is before set_at' do
|
||||||
|
freeze_time
|
||||||
|
user_status = UserStatus.new(user: user, set_at: Time.zone.now, ends_at: 1.hour.ago)
|
||||||
|
user_status.valid?
|
||||||
|
expect(user_status.errors[:ends_at]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
86
spec/requests/user_status_controller_spec.rb
Normal file
86
spec/requests/user_status_controller_spec.rb
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe UserStatusController do
|
||||||
|
describe '#set' do
|
||||||
|
it 'requires user to be logged in' do
|
||||||
|
put "/user-status.json", params: { description: "off to dentist" }
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 if the feature is disabled" do
|
||||||
|
user = Fabricate(:user)
|
||||||
|
sign_in(user)
|
||||||
|
SiteSetting.enable_user_status = false
|
||||||
|
|
||||||
|
put "/user-status.json", params: { description: "off" }
|
||||||
|
|
||||||
|
expect(response.status).to eq(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'feature is enabled and user is logged in' do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
SiteSetting.enable_user_status = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets user status" do
|
||||||
|
status = "off to dentist"
|
||||||
|
put "/user-status.json", params: { description: status }
|
||||||
|
expect(user.user_status.description).to eq(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'the description parameter is mandatory' do
|
||||||
|
put "/user-status.json", params: {}
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "following calls update status" do
|
||||||
|
status = "off to dentist"
|
||||||
|
put "/user-status.json", params: { description: status }
|
||||||
|
user.reload
|
||||||
|
expect(user.user_status.description).to eq(status)
|
||||||
|
|
||||||
|
new_status = "working"
|
||||||
|
put "/user-status.json", params: { description: new_status }
|
||||||
|
user.reload
|
||||||
|
expect(user.user_status.description).to eq(new_status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#clear' do
|
||||||
|
it 'requires you to be logged in' do
|
||||||
|
delete "/user-status.json"
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 if the feature is disabled" do
|
||||||
|
user = Fabricate(:user)
|
||||||
|
sign_in(user)
|
||||||
|
SiteSetting.enable_user_status = false
|
||||||
|
|
||||||
|
delete "/user-status.json"
|
||||||
|
|
||||||
|
expect(response.status).to eq(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'feature is enabled and user is logged in' do
|
||||||
|
fab!(:user_status) { Fabricate(:user_status, description: "off to dentist") }
|
||||||
|
fab!(:user) { Fabricate(:user, user_status: user_status) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
SiteSetting.enable_user_status = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "clears user status" do
|
||||||
|
delete "/user-status.json"
|
||||||
|
|
||||||
|
user.reload
|
||||||
|
expect(user.user_status).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -182,4 +182,27 @@ RSpec.describe CurrentUserSerializer do
|
||||||
expect(pending_posts_count).to eq 3
|
expect(pending_posts_count).to eq 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#status" do
|
||||||
|
fab!(:user_status) { Fabricate(:user_status) }
|
||||||
|
fab!(:user) { Fabricate(:user, user_status: user_status) }
|
||||||
|
let(:serializer) { described_class.new(user, scope: Guardian.new(user), root: false) }
|
||||||
|
|
||||||
|
it "serializes when enabled" do
|
||||||
|
SiteSetting.enable_user_status = true
|
||||||
|
|
||||||
|
json = serializer.as_json
|
||||||
|
|
||||||
|
expect(json[:status]).to_not be_nil do |status|
|
||||||
|
expect(status.description).to eq(user_status.description)
|
||||||
|
expect(status.emoji).to eq(user_status.emoji)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't serialize when disabled" do
|
||||||
|
SiteSetting.enable_user_status = false
|
||||||
|
json = serializer.as_json
|
||||||
|
expect(json.keys).not_to include :status
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,4 +70,27 @@ describe UserCardSerializer do
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#status" do
|
||||||
|
fab!(:user_status) { Fabricate(:user_status) }
|
||||||
|
fab!(:user) { Fabricate(:user, user_status: user_status) }
|
||||||
|
let(:serializer) { described_class.new(user, scope: Guardian.new(user), root: false) }
|
||||||
|
|
||||||
|
it "serializes when enabled" do
|
||||||
|
SiteSetting.enable_user_status = true
|
||||||
|
|
||||||
|
json = serializer.as_json
|
||||||
|
|
||||||
|
expect(json[:status]).to_not be_nil do |status|
|
||||||
|
expect(status.description).to eq(user_status.description)
|
||||||
|
expect(status.emoji).to eq(user_status.emoji)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't serialize when disabled" do
|
||||||
|
SiteSetting.enable_user_status = false
|
||||||
|
json = serializer.as_json
|
||||||
|
expect(json.keys).not_to include :status
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user