From 6f177e0524f99d6e107f5d7819868fa48f840ace Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sun, 19 Jan 2025 21:43:20 +0000 Subject: [PATCH] Implement user ignores --- pkg/hicli/json-commands.go | 14 +++++ web/src/api/rpc.ts | 4 ++ web/src/icons/block.svg | 1 + web/src/ui/rightpanel/RightPanel.css | 2 +- web/src/ui/rightpanel/UserModeration.tsx | 75 ++++++++++++++++++++---- 5 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 web/src/icons/block.svg diff --git a/pkg/hicli/json-commands.go b/pkg/hicli/json-commands.go index dca1ea7..1fa09a7 100644 --- a/pkg/hicli/json-commands.go +++ b/pkg/hicli/json-commands.go @@ -66,6 +66,15 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any return unmarshalAndCall(req.Data, func(params *sendStateEventParams) (id.EventID, error) { return h.SetState(ctx, params.RoomID, params.EventType, params.StateKey, params.Content) }) + case "get_account_data": + return unmarshalAndCall(req.Data, func(params *getAccountDataParams) (*map[string]any, error) { + var result map[string]any + if params.RoomID != "" { + return &result, h.Client.GetRoomAccountData(ctx, params.RoomID, params.Type, &result) + } else { + return &result, h.Client.GetAccountData(ctx, params.Type, &result) + } + }) case "set_account_data": return unmarshalAndCall(req.Data, func(params *setAccountDataParams) (bool, error) { if params.RoomID != "" { @@ -262,6 +271,11 @@ type sendStateEventParams struct { Content json.RawMessage `json:"content"` } +type getAccountDataParams struct { + RoomID id.RoomID `json:"room_id,omitempty"` + Type string `json:"type"` +} + type setAccountDataParams struct { RoomID id.RoomID `json:"room_id,omitempty"` Type string `json:"type"` diff --git a/web/src/api/rpc.ts b/web/src/api/rpc.ts index 95fb019..43534c9 100644 --- a/web/src/api/rpc.ts +++ b/web/src/api/rpc.ts @@ -167,6 +167,10 @@ export default abstract class RPCClient { return this.request("set_state", { room_id, type, state_key, content }) } + getAccountData(type: EventType, roomID?: RoomID): Promise { + return this.request("get_account_data", { type, roomID }) + } + setAccountData(type: EventType, content: unknown, room_id?: RoomID): Promise { return this.request("set_account_data", { type, content, room_id }) } diff --git a/web/src/icons/block.svg b/web/src/icons/block.svg new file mode 100644 index 0000000..5e88bbd --- /dev/null +++ b/web/src/icons/block.svg @@ -0,0 +1 @@ + diff --git a/web/src/ui/rightpanel/RightPanel.css b/web/src/ui/rightpanel/RightPanel.css index f709361..b652b3c 100644 --- a/web/src/ui/rightpanel/RightPanel.css +++ b/web/src/ui/rightpanel/RightPanel.css @@ -205,7 +205,7 @@ div.right-panel-content.user { fill: var(--error-color) } } - .invite { + .positive { color: var(--primary-color); svg { fill: var(--primary-color) diff --git a/web/src/ui/rightpanel/UserModeration.tsx b/web/src/ui/rightpanel/UserModeration.tsx index b6a9caf..5ff9d05 100644 --- a/web/src/ui/rightpanel/UserModeration.tsx +++ b/web/src/ui/rightpanel/UserModeration.tsx @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { use } from "react" +import { use, useEffect, useState } from "react" import Client from "@/api/client.ts" import { RoomStateStore } from "@/api/statestore" import { MemDBEvent, MemberEventContent, Membership } from "@/api/types" @@ -21,6 +21,7 @@ import { ModalContext } from "@/ui/modal" import { RoomContext } from "@/ui/roomview/roomcontext.ts" import ConfirmWithMessageModal from "@/ui/timeline/menu/ConfirmWithMessageModal.tsx" import { getPowerLevels } from "@/ui/timeline/menu/util.ts" +import Block from "@/icons/block.svg?react" import Gavel from "@/icons/gavel.svg?react" import PersonAdd from "@/icons/person-add.svg?react" import PersonRemove from "@/icons/person-remove.svg?react" @@ -32,13 +33,62 @@ interface UserModerationProps { member: MemDBEvent | null; } +interface IgnoredUsersType { + ignored_users: Record; +} + +const UserIgnoreButton = ({ userID, client }: { userID: string; client: Client }) => { + const [ignoredUsers, setIgnoredUsers] = useState(null) + useEffect(() => { + // Get blocked user list + client.rpc.getAccountData("m.ignored_user_list").then((data) => { + const parsedData = data as IgnoredUsersType + if (data !== ignoredUsers || !("ignored_users" in parsedData)) { + return + } + setIgnoredUsers(parsedData) + }).catch((e) => { + console.error("Failed to get ignored users", e) + }) + }) + + const isIgnored = ignoredUsers?.ignored_users[userID] + const ignoreUser = () => { + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + newIgnoredUsers.ignored_users[userID] = {} + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).then(() => { + setIgnoredUsers(newIgnoredUsers) + }).catch((e) => { + console.error("Failed to ignore user", e) + }) + } + const unignoreUser = () => { + const newIgnoredUsers = { ...(ignoredUsers || { ignored_users: {}}) } + delete newIgnoredUsers.ignored_users[userID] + client.rpc.setAccountData("m.ignored_user_list", newIgnoredUsers).then(() => { + setIgnoredUsers(newIgnoredUsers) + }).catch((e) => { + console.error("Failed to unignore user", e) + }) + } + + return ( + + ) +} + const UserModeration = ({ userID, client, member }: UserModerationProps) => { const roomCtx = use(RoomContext) - if(!roomCtx) { - return null // There is no room context, moderation is not an applicable context. - } const openModal = use(ModalContext) const hasPl = (action: "invite" | "kick" | "ban") => { + if(!roomCtx) { + return false // no room context + } const [pls, ownPL] = getPowerLevels(roomCtx.store, client) const actionPL = pls[action] ?? pls.state_default ?? 50 const otherUserPl = pls.users?.[userID] ?? pls.users_default ?? 0 @@ -88,42 +138,43 @@ const UserModeration = ({ userID, client, member }: UserModerationProps) => { }) } } - const membership = member?.content.membership || "leave" + return (

Moderation

- {(["knock", "leave"].includes(membership) || !member) && hasPl("invite") && ( - )} - {["knock", "invite"].includes(membership) && hasPl("kick") && ( + {roomCtx && ["knock", "invite"].includes(membership) && hasPl("kick") && ( )} - {membership === "join" && hasPl("kick") && ( + {roomCtx && membership === "join" && hasPl("kick") && ( )} - {membership !== "ban" && hasPl("ban") && ( + {roomCtx && membership !== "ban" && hasPl("ban") && ( )} - {membership === "ban" && hasPl("ban") && ( - )} +
)