mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
Implement user ignores
This commit is contained in:
parent
bef3e43de8
commit
6f177e0524
5 changed files with 83 additions and 13 deletions
|
@ -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"`
|
||||
|
|
|
@ -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<unknown> {
|
||||
return this.request("get_account_data", { type, roomID })
|
||||
}
|
||||
|
||||
setAccountData(type: EventType, content: unknown, room_id?: RoomID): Promise<boolean> {
|
||||
return this.request("set_account_data", { type, content, room_id })
|
||||
}
|
||||
|
|
1
web/src/icons/block.svg
Normal file
1
web/src/icons/block.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5E6267"><path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q54 0 104-17.5t92-50.5L228-676q-33 42-50.5 92T160-480q0 134 93 227t227 93Zm252-124q33-42 50.5-92T800-480q0-134-93-227t-227-93q-54 0-104 17.5T284-732l448 448Z"/></svg>
|
After Width: | Height: | Size: 477 B |
|
@ -205,7 +205,7 @@ div.right-panel-content.user {
|
|||
fill: var(--error-color)
|
||||
}
|
||||
}
|
||||
.invite {
|
||||
.positive {
|
||||
color: var(--primary-color);
|
||||
svg {
|
||||
fill: var(--primary-color)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
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<string, object>;
|
||||
}
|
||||
|
||||
const UserIgnoreButton = ({ userID, client }: { userID: string; client: Client }) => {
|
||||
const [ignoredUsers, setIgnoredUsers] = useState<IgnoredUsersType | null>(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 (
|
||||
<button
|
||||
className={"moderation-actions " + (isIgnored ? "positive" : "dangerous")}
|
||||
onClick={isIgnored ? unignoreUser : ignoreUser}>
|
||||
<Block/>
|
||||
<span>{isIgnored ? "Unignore" : "Ignore"}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="user-moderation">
|
||||
<h4>Moderation</h4>
|
||||
<div className="moderation-actions">
|
||||
{(["knock", "leave"].includes(membership) || !member) && hasPl("invite") && (
|
||||
<button className="moderation-action invite" onClick={runAction("invite")}>
|
||||
{roomCtx && (["knock", "leave"].includes(membership) || !member) && hasPl("invite") && (
|
||||
<button className="moderation-action positive" onClick={runAction("invite")}>
|
||||
<PersonAdd />
|
||||
<span>{membership === "knock" ? "Accept request to join" : "Invite"}</span>
|
||||
</button>
|
||||
)}
|
||||
{["knock", "invite"].includes(membership) && hasPl("kick") && (
|
||||
{roomCtx && ["knock", "invite"].includes(membership) && hasPl("kick") && (
|
||||
<button className="moderation-action dangerous" onClick={runAction("leave")}>
|
||||
<PersonRemove />
|
||||
<span>{membership === "invite" ? "Revoke invitation" : "Reject join request"}</span>
|
||||
</button>
|
||||
)}
|
||||
{membership === "join" && hasPl("kick") && (
|
||||
{roomCtx && membership === "join" && hasPl("kick") && (
|
||||
<button className="moderation-action dangerous" onClick={runAction("leave")}>
|
||||
<PersonRemove />
|
||||
<span>Kick</span>
|
||||
</button>
|
||||
)}
|
||||
{membership !== "ban" && hasPl("ban") && (
|
||||
{roomCtx && membership !== "ban" && hasPl("ban") && (
|
||||
<button className="moderation-action dangerous" onClick={runAction("ban")}>
|
||||
<Gavel />
|
||||
<span>Ban</span>
|
||||
</button>
|
||||
)}
|
||||
{membership === "ban" && hasPl("ban") && (
|
||||
<button className="moderation-action invite" onClick={runAction("leave")}>
|
||||
{roomCtx && membership === "ban" && hasPl("ban") && (
|
||||
<button className="moderation-action positive" onClick={runAction("leave")}>
|
||||
<Gavel />
|
||||
<span>Unban</span>
|
||||
</button>
|
||||
)}
|
||||
<UserIgnoreButton userID={userID} client={client} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue