diff --git a/client/html/user_edit.tpl b/client/html/user_edit.tpl
index d518aabb..22dcd835 100644
--- a/client/html/user_edit.tpl
+++ b/client/html/user_edit.tpl
@@ -68,6 +68,12 @@
<% } %>
+
+ <% if (ctx.canEditBlocklist) { %>
+
+ <%= ctx.makeTextInput({text: 'Blocklist'}) %>
+
+ <% } %>
diff --git a/client/js/controllers/user_controller.js b/client/js/controllers/user_controller.js
index 8cf46584..bd41aa1e 100644
--- a/client/js/controllers/user_controller.js
+++ b/client/js/controllers/user_controller.js
@@ -89,6 +89,7 @@ class UserController {
canEditAvatar: api.hasPrivilege(
`users:edit:${infix}:avatar`
),
+ canEditBlocklist: api.hasPrivilege(`users:edit:${infix}:blocklist`),
canEditAnything: api.hasPrivilege(`users:edit:${infix}`),
canListTokens: api.hasPrivilege(
`userTokens:list:${infix}`
diff --git a/client/js/models/user.js b/client/js/models/user.js
index 28dc3efe..40b6ebec 100644
--- a/client/js/models/user.js
+++ b/client/js/models/user.js
@@ -3,11 +3,19 @@
const api = require("../api.js");
const uri = require("../util/uri.js");
const events = require("../events.js");
+const misc = require("../util/misc.js");
class User extends events.EventTarget {
constructor() {
+ const TagList = require("./tag_list.js");
+
super();
this._orig = {};
+
+ for (let obj of [this, this._orig]) {
+ obj._blocklist = new TagList();
+ }
+
this._updateFromResponse({});
}
@@ -71,6 +79,10 @@ class User extends events.EventTarget {
throw "Invalid operation";
}
+ get blocklist() {
+ return this._blocklist;
+ }
+
set name(value) {
this._name = value;
}
@@ -95,6 +107,10 @@ class User extends events.EventTarget {
this._password = value;
}
+ set blocklist(value) {
+ this._blocklist = value || "";
+ }
+
static fromResponse(response) {
const ret = new User();
ret._updateFromResponse(response);
@@ -121,6 +137,11 @@ class User extends events.EventTarget {
if (this._rank !== this._orig._rank) {
detail.rank = this._rank;
}
+ if (misc.arraysDiffer(this._blocklist, this._orig._blocklist)) {
+ detail.blocklist = this._blocklist.map(
+ (relation) => relation.names[0]
+ );
+ }
if (this._avatarStyle !== this._orig._avatarStyle) {
detail.avatarStyle = this._avatarStyle;
}
@@ -187,6 +208,10 @@ class User extends events.EventTarget {
_dislikedPostCount: response.dislikedPostCount,
};
+ for (let obj of [this, this._orig]) {
+ obj._blocklist.sync(response.blocklist);
+ }
+
Object.assign(this, map);
Object.assign(this._orig, map);
diff --git a/client/js/views/user_edit_view.js b/client/js/views/user_edit_view.js
index 4886726a..bee0bb4f 100644
--- a/client/js/views/user_edit_view.js
+++ b/client/js/views/user_edit_view.js
@@ -4,6 +4,8 @@ const events = require("../events.js");
const api = require("../api.js");
const views = require("../util/views.js");
const FileDropperControl = require("../controls/file_dropper_control.js");
+const TagInputControl = require("../controls/tag_input_control.js")
+const misc = require("../util/misc.js");
const template = views.getTemplate("user-edit");
@@ -41,6 +43,13 @@ class UserEditView extends events.EventTarget {
});
}
+ if (this._blocklistFieldNode) {
+ new TagInputControl(
+ this._blocklistFieldNode,
+ this._user.blocklist
+ );
+ }
+
this._formNode.addEventListener("submit", (e) => this._evtSubmit(e));
}
@@ -83,6 +92,10 @@ class UserEditView extends events.EventTarget {
? this._rankInputNode.value
: undefined,
+ blocklist: this._blocklistFieldNode
+ ? misc.splitByWhitespace(this._blocklistFieldNode.value)
+ : undefined,
+
avatarStyle: this._avatarStyleInputNode
? this._avatarStyleInputNode.value
: undefined,
@@ -101,6 +114,10 @@ class UserEditView extends events.EventTarget {
return this._hostNode.querySelector("form");
}
+ get _blocklistFieldNode() {
+ return this._formNode.querySelector(".blocklist input");
+ }
+
get _rankInputNode() {
return this._formNode.querySelector("[name=rank]");
}