Call Guest Access, give user the option to change the acces level so they can generate a call link. (#12401)
* Ask the user to change the room access settings if they click the create link button. Signed-off-by: Timo K <toger5@hotmail.de> * disable call button if appropriate. Signed-off-by: Timo K <toger5@hotmail.de> * Add tests Refactor tests to be in CallGuestLinkButton-test instead of the RoomHeader Signed-off-by: Timo K <toger5@hotmail.de> * add test for: no button if cannot change join rule and room not public nor knock Signed-off-by: Timo K <toger5@hotmail.de> * fix tests Signed-off-by: Timo K <toger5@hotmail.de> * add JoinRuleDialog tests Signed-off-by: Timo K <toger5@hotmail.de> * move spy into before each Signed-off-by: Timo K <toger5@hotmail.de> * Update src/i18n/strings/en_EN.json Co-authored-by: Robin <robin@robin.town> * remove inline css and update modal style Signed-off-by: Timo K <toger5@hotmail.de> * Update src/i18n/strings/en_EN.json Co-authored-by: Robin <robin@robin.town> * Update src/i18n/strings/en_EN.json Co-authored-by: Robin <robin@robin.town> * Invite state was not reactive. Changing power level did not update the ui. Signed-off-by: Timo K <toger5@hotmail.de> * linter Signed-off-by: Timo K <toger5@hotmail.de> * make useGuestAccessInformation use useRoomState Signed-off-by: Timo K <toger5@hotmail.de> * fix tests and simplify logic * fix tests * review Signed-off-by: Timo K <toger5@hotmail.de> --------- Signed-off-by: Timo K <toger5@hotmail.de> Co-authored-by: Robin <robin@robin.town>
This commit is contained in:
parent
59395abb6b
commit
d35fce198c
11 changed files with 588 additions and 175 deletions
58
src/hooks/room/useGuestAccessInformation.ts
Normal file
58
src/hooks/room/useGuestAccessInformation.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
import { useRoomState } from "../useRoomState";
|
||||
|
||||
interface GuestAccessInformation {
|
||||
canInviteGuests: boolean;
|
||||
guestSpaUrl?: string;
|
||||
isRoomJoinable: () => boolean;
|
||||
canInvite: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to retrieve the guest access related information for a room.
|
||||
* @param room
|
||||
* @returns The GuestAccessInformation which helps decide what options the user should be given.
|
||||
*/
|
||||
export const useGuestAccessInformation = (room: Room): GuestAccessInformation => {
|
||||
const guestSpaUrl = useMemo(() => {
|
||||
return SdkConfig.get("element_call").guest_spa_url;
|
||||
}, []);
|
||||
|
||||
// We use the direct function only in functions triggered by user interaction to avoid computation on every render.
|
||||
const { joinRule, canInvite, canChangeJoinRule } = useRoomState(room, (roomState) => ({
|
||||
joinRule: room.getJoinRule(),
|
||||
canInvite: room.canInvite(room.myUserId),
|
||||
canChangeJoinRule: roomState.maySendStateEvent(EventType.RoomJoinRules, room.myUserId),
|
||||
}));
|
||||
const isRoomJoinable = useMemo(
|
||||
() => joinRule === JoinRule.Public || (joinRule === JoinRule.Knock && canInvite),
|
||||
[canInvite, joinRule],
|
||||
);
|
||||
const canInviteGuests = useMemo(
|
||||
() => (canChangeJoinRule || isRoomJoinable) && guestSpaUrl !== undefined,
|
||||
[canChangeJoinRule, isRoomJoinable, guestSpaUrl],
|
||||
);
|
||||
|
||||
const isRoomJoinableFunction = (): boolean =>
|
||||
room.getJoinRule() === JoinRule.Public || (joinRule === JoinRule.Knock && room.canInvite(room.myUserId));
|
||||
return { canInviteGuests, guestSpaUrl, isRoomJoinable: isRoomJoinableFunction, canInvite };
|
||||
};
|
|
@ -14,10 +14,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { JoinRule, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { CallType } from "matrix-js-sdk/src/webrtc/call";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { useFeatureEnabled } from "../useSettings";
|
||||
import SdkConfig from "../../SdkConfig";
|
||||
|
@ -27,7 +26,7 @@ import { useWidgets } from "../../components/views/right_panel/RoomSummaryCard";
|
|||
import { WidgetType } from "../../widgets/WidgetType";
|
||||
import { useCall, useConnectionState, useParticipantCount } from "../useCall";
|
||||
import { useRoomMemberCount } from "../useRoomMembers";
|
||||
import { Call, ConnectionState, ElementCall } from "../../models/Call";
|
||||
import { ConnectionState, ElementCall } from "../../models/Call";
|
||||
import { placeCall } from "../../utils/room/placeCall";
|
||||
import { Container, WidgetLayoutStore } from "../../stores/widgets/WidgetLayoutStore";
|
||||
import { useRoomState } from "../useRoomState";
|
||||
|
@ -40,8 +39,8 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
|
|||
import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import { CallStore, CallStoreEvent } from "../../stores/CallStore";
|
||||
import { calculateRoomVia } from "../../utils/permalinks/Permalinks";
|
||||
import { isVideoRoom } from "../../utils/video-rooms";
|
||||
import { useGuestAccessInformation } from "./useGuestAccessInformation";
|
||||
|
||||
export enum PlatformCallType {
|
||||
ElementCall,
|
||||
|
@ -81,8 +80,6 @@ export const useRoomCall = (
|
|||
videoCallClick(evt: React.MouseEvent | undefined, selectedType: PlatformCallType): void;
|
||||
toggleCallMaximized: () => void;
|
||||
isViewingCall: boolean;
|
||||
generateCallLink: () => URL;
|
||||
canGenerateCallLink: boolean;
|
||||
isConnectedToCall: boolean;
|
||||
hasActiveCallSession: boolean;
|
||||
callOptions: PlatformCallType[];
|
||||
|
@ -93,10 +90,6 @@ export const useRoomCall = (
|
|||
return SdkConfig.get("element_call").use_exclusively;
|
||||
}, []);
|
||||
|
||||
const guestSpaUrl = useMemo(() => {
|
||||
return SdkConfig.get("element_call").guest_spa_url;
|
||||
}, []);
|
||||
|
||||
const hasLegacyCall = useEventEmitterState(
|
||||
LegacyCallHandler.instance,
|
||||
LegacyCallHandlerEvent.CallsChanged,
|
||||
|
@ -123,11 +116,9 @@ export const useRoomCall = (
|
|||
// room
|
||||
const memberCount = useRoomMemberCount(room);
|
||||
|
||||
const [mayEditWidgets, mayCreateElementCalls, canJoinWithoutInvite] = useRoomState(room, () => [
|
||||
const [mayEditWidgets, mayCreateElementCalls] = useRoomState(room, () => [
|
||||
room.currentState.mayClientSendStateEvent("im.vector.modular.widgets", room.client),
|
||||
room.currentState.mayClientSendStateEvent(ElementCall.MEMBER_EVENT_TYPE.name, room.client),
|
||||
room.getJoinRule() === "public" || room.getJoinRule() === JoinRule.Knock,
|
||||
/*|| room.getJoinRule() === JoinRule.Restricted <- rule for joining via token?*/
|
||||
]);
|
||||
|
||||
// The options provided to the RoomHeader.
|
||||
|
@ -180,17 +171,16 @@ export const useRoomCall = (
|
|||
useEffect(() => {
|
||||
updateWidgetState();
|
||||
}, [room, jitsiWidget, groupCall, updateWidgetState]);
|
||||
const [activeCalls, setActiveCalls] = useState<Call[]>(Array.from(CallStore.instance.activeCalls));
|
||||
useEventEmitter(CallStore.instance, CallStoreEvent.ActiveCalls, () => {
|
||||
setActiveCalls(Array.from(CallStore.instance.activeCalls));
|
||||
});
|
||||
const [canPinWidget, setCanPinWidget] = useState(false);
|
||||
const [widgetPinned, setWidgetPinned] = useState(false);
|
||||
// We only want to prompt to pin the widget if it's not element call based.
|
||||
const isECWidget = WidgetType.CALL.matches(widget?.type ?? "");
|
||||
const promptPinWidget = !isECWidget && canPinWidget && !widgetPinned;
|
||||
const userId = room.client.getUserId();
|
||||
const canInviteToRoom = userId ? room.canInvite(userId) : false;
|
||||
const activeCalls = useEventEmitterState(CallStore.instance, CallStoreEvent.ActiveCalls, () =>
|
||||
Array.from(CallStore.instance.activeCalls),
|
||||
);
|
||||
const { canInviteGuests } = useGuestAccessInformation(room);
|
||||
|
||||
const state = useMemo((): State => {
|
||||
if (activeCalls.find((call) => call.roomId != room.roomId)) {
|
||||
return State.Ongoing;
|
||||
|
@ -201,9 +191,7 @@ export const useRoomCall = (
|
|||
if (hasLegacyCall) {
|
||||
return State.Ongoing;
|
||||
}
|
||||
const canCallAlone =
|
||||
canInviteToRoom && (room.getJoinRule() === "public" || room.getJoinRule() === JoinRule.Knock);
|
||||
if (!(memberCount > 1 || canCallAlone)) {
|
||||
if (memberCount <= 1 && !canInviteGuests) {
|
||||
return State.NoOneHere;
|
||||
}
|
||||
|
||||
|
@ -213,7 +201,7 @@ export const useRoomCall = (
|
|||
return State.NoCall;
|
||||
}, [
|
||||
activeCalls,
|
||||
canInviteToRoom,
|
||||
canInviteGuests,
|
||||
hasGroupCall,
|
||||
hasJitsiWidget,
|
||||
hasLegacyCall,
|
||||
|
@ -222,7 +210,7 @@ export const useRoomCall = (
|
|||
mayEditWidgets,
|
||||
memberCount,
|
||||
promptPinWidget,
|
||||
room,
|
||||
room.roomId,
|
||||
]);
|
||||
|
||||
const voiceCallClick = useCallback(
|
||||
|
@ -278,26 +266,6 @@ export const useRoomCall = (
|
|||
});
|
||||
}, [isViewingCall, room.roomId]);
|
||||
|
||||
const generateCallLink = useCallback(() => {
|
||||
if (!canJoinWithoutInvite)
|
||||
throw new Error("Cannot create link for room that users can not join without invite.");
|
||||
if (!guestSpaUrl) throw new Error("No guest SPA url for external links provided.");
|
||||
const url = new URL(guestSpaUrl);
|
||||
url.pathname = "/room/";
|
||||
// Set params for the sharable url
|
||||
url.searchParams.set("roomId", room.roomId);
|
||||
if (room.hasEncryptionStateEvent()) url.searchParams.set("perParticipantE2EE", "true");
|
||||
for (const server of calculateRoomVia(room)) {
|
||||
url.searchParams.set("viaServers", server);
|
||||
}
|
||||
|
||||
// Move params into hash
|
||||
url.hash = "/" + room.name + url.search;
|
||||
url.search = "";
|
||||
|
||||
logger.info("Generated element call external url:", url);
|
||||
return url;
|
||||
}, [canJoinWithoutInvite, guestSpaUrl, room]);
|
||||
/**
|
||||
* We've gone through all the steps
|
||||
*/
|
||||
|
@ -308,8 +276,6 @@ export const useRoomCall = (
|
|||
videoCallClick,
|
||||
toggleCallMaximized: toggleCallMaximized,
|
||||
isViewingCall: isViewingCall,
|
||||
generateCallLink,
|
||||
canGenerateCallLink: guestSpaUrl !== undefined && canJoinWithoutInvite,
|
||||
isConnectedToCall: isConnectedToCall,
|
||||
hasActiveCallSession: hasActiveCallSession,
|
||||
callOptions,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue