Implement knocking on rooms

This commit is contained in:
nexy7574 2025-04-06 01:03:57 +01:00
parent c2b12b1a88
commit 17e2b91230
No known key found for this signature in database
8 changed files with 66 additions and 20 deletions

View file

@ -72,14 +72,14 @@ require (
golang.org/x/mod v0.24.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/tools v0.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/mautrix v0.23.2 // indirect
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38 // indirect
mvdan.cc/xurls/v2 v2.6.0 // indirect
)

View file

@ -221,8 +221,8 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@ -260,7 +260,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mautrix v0.23.2 h1:Bo3tPrQJwkxyL7aMmy/T+d2tqIrypZjHqeHe8fyeAOg=
maunium.net/go/mautrix v0.23.2/go.mod h1:pCYLHmo02Jauak/9VlTkbGPrBMvLXsGqTGMNOx+L2PE=
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38 h1:fIe2+kYndm3Mm/DwQ4FsODk2DjrLeEeW7tKtZjyERqM=
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38/go.mod h1:pCYLHmo02Jauak/9VlTkbGPrBMvLXsGqTGMNOx+L2PE=
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=

4
go.mod
View file

@ -27,7 +27,7 @@ require (
golang.org/x/text v0.23.0
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mauflag v1.0.0
maunium.net/go/mautrix v0.23.2
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38
mvdan.cc/xurls/v2 v2.6.0
)
@ -42,6 +42,6 @@ require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/sys v0.32.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)

8
go.sum
View file

@ -86,8 +86,8 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
@ -99,7 +99,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.23.2 h1:Bo3tPrQJwkxyL7aMmy/T+d2tqIrypZjHqeHe8fyeAOg=
maunium.net/go/mautrix v0.23.2/go.mod h1:pCYLHmo02Jauak/9VlTkbGPrBMvLXsGqTGMNOx+L2PE=
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38 h1:fIe2+kYndm3Mm/DwQ4FsODk2DjrLeEeW7tKtZjyERqM=
maunium.net/go/mautrix v0.23.3-0.20250405234116-e675a3c09c38/go.mod h1:pCYLHmo02Jauak/9VlTkbGPrBMvLXsGqTGMNOx+L2PE=
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=

View file

@ -173,6 +173,13 @@ func (h *HiClient) handleJSONCommand(ctx context.Context, req *JSONCommand) (any
Reason: params.Reason,
})
})
case "knock_room":
return unmarshalAndCall(req.Data, func(params *joinRoomParams) (*mautrix.RespKnockRoom, error) {
return h.Client.KnockRoom(ctx, params.RoomIDOrAlias, &mautrix.ReqKnockRoom{
Via: params.Via,
Reason: params.Reason,
})
})
case "leave_room":
return unmarshalAndCall(req.Data, func(params *leaveRoomParams) (*mautrix.RespLeaveRoom, error) {
return h.Client.LeaveRoom(ctx, params.RoomID, &mautrix.ReqLeave{Reason: params.Reason})

View file

@ -265,6 +265,10 @@ export default abstract class RPCClient {
return this.request("join_room", { room_id_or_alias, via, reason })
}
knockRoom(room_id_or_alias: RoomID | RoomAlias, via?: string[], reason?: string): Promise<RespRoomJoin> {
return this.request("knock_room", { room_id_or_alias, via, reason })
}
leaveRoom(room_id: RoomID, reason?: string): Promise<Record<string, never>> {
return this.request("leave_room", { room_id, reason })
}

View file

@ -316,6 +316,7 @@ export interface RoomSummary {
room_type: RoomType
topic?: string
world_readable: boolean
allowed_room_ids?: RoomID[]
}
export interface RespRoomJoin {

View file

@ -42,19 +42,34 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
const [loading, setLoading] = useState(false)
const [buttonClicked, setButtonClicked] = useState(false)
const [error, setError] = useState<string | null>(null)
const [knockRequest, setKnockRequest] = useState<string | null>(null)
const doJoinRoom = () => {
let realVia = via
if (!via?.length && invite?.invited_by) {
realVia = [getServerName(invite.invited_by)]
}
setButtonClicked(true)
client.rpc.joinRoom(alias || roomID, alias ? undefined : realVia).then(
() => console.info("Successfully joined", roomID),
err => {
setError(`Failed to join room: ${err}`)
setButtonClicked(false)
},
)
if (requiresKnock) {
client.rpc.knockRoom(alias || roomID, alias ? undefined : realVia, knockRequest || undefined).then(
() => {
setButtonClicked(false)
mainScreen.clearActiveRoom()
},
err => {
setError(`Failed to knock: ${err}`)
setButtonClicked(false)
},
)
return
} else {
client.rpc.joinRoom(alias || roomID, alias ? undefined : realVia).then(
() => console.info("Successfully joined", roomID),
err => {
setError(`Failed to join room: ${err}`)
setButtonClicked(false)
},
)
}
}
const doRejectInvite = () => {
setButtonClicked(true)
@ -87,6 +102,19 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
const topic = summary?.topic ?? invite?.topic ?? ""
const showInviteAvatars = usePreference(client.store, null, "show_invite_avatars")
const noAvatarPreview = invite && !showInviteAvatars
const joinRule = summary?.join_rule ?? invite?.join_rule ?? "invite"
let requiresKnock = ["knock", "knock_restricted"].includes(joinRule) && !invite
if (joinRule === "knock_restricted" && !invite) {
for (const roomID of summary?.allowed_room_ids ?? []) {
if (client.store.rooms.has(roomID)) {
console.log("RoomPreview: allowed room ID is already joined", roomID)
requiresKnock = false
break
}
}
}
const acceptAction = invite ? "Accept" : (requiresKnock ? "Ask to join" : "Join room")
return <div className="room-view preview">
<div className="preview-inner">
{invite?.invited_by && !invite.dm_user_id ? <div className="inviter-info">
@ -152,6 +180,12 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
</table>
</details>}
{invite?.invited_by && <MutualRooms client={client} userID={invite.invited_by}/>}
{requiresKnock && <div className="knock-input">
<textarea
onChange={event => setKnockRequest(event.currentTarget.value)}
placeholder="Why do you want to join this room?">
</textarea>
</div>}
<div className="buttons">
{invite && <button
disabled={buttonClicked}
@ -162,7 +196,7 @@ const RoomPreview = ({ roomID, via, alias, invite }: RoomPreviewProps) => {
disabled={buttonClicked}
className="primary-color-button"
onClick={doJoinRoom}
>{invite ? "Accept" : "Join room"}</button>
>{acceptAction}</button>
</div>
{error && <div className="error">
<ErrorIcon color="var(--error-color)"/>