Skip to content

Commit

Permalink
feat(room_access_link): add room access by link
Browse files Browse the repository at this point in the history
  • Loading branch information
marc.sirisak committed Nov 5, 2024
1 parent e2fc0fd commit ce3d1d0
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 12 deletions.
113 changes: 113 additions & 0 deletions src/tchap/components/views/rooms/TchapRoomLinkAccess.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { EventTimeline, EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix";

import React, { useEffect, useState } from 'react';
import LabelledToggleSwitch from 'matrix-react-sdk/src/components/views/elements/LabelledToggleSwitch';
import { _t } from 'matrix-react-sdk/src/languageHandler';
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
import { randomString } from 'matrix-js-sdk/src/randomstring';
import TchapRoomUtils from '../../../util/TchapRoomUtils';
import { makeRoomPermalink } from "matrix-react-sdk/src/utils/permalinks/Permalinks";
import { TchapRoomType } from "../../../@types/tchap";
import { RoomJoinRulesEventContent } from "matrix-js-sdk/lib/types";

interface ITchapRoomLinkAccessProps {
room: Room,
onBeforeChangeCallback: Function
}

export default function TchapRoomLinkAccess({room, onBeforeChangeCallback}: ITchapRoomLinkAccessProps) : JSX.Element {

const [isLinkSharingActivated, setIsLinkSharingActivated] = useState(false);
const [linkSharingUrl, setLinkSharingUrl] = useState("");

// Getting the initial value of the link. We need to check if it was previsouly activated or not
const initialLinkSharingValue = () => {
const isActivated = isJoinRulePublic() || TchapRoomUtils.getTchapRoomType(room) === TchapRoomType.Forum

if (isActivated) {
const link = makeRoomPermalink(room.client, room.roomId);
setLinkSharingUrl(link);
}
setIsLinkSharingActivated(isActivated)
// updating the parent join rule options
onBeforeChangeCallback(isActivated, true);
}

useEffect(() => {
initialLinkSharingValue();
}, []);

// Create the permalink to share
const _setUpRoomByLink = async () => {
const client = MatrixClientPeg.get()!;
try {
// create an alias if not existing
if (!room.getCanonicalAlias()) {
const aliasName = (room.name?.replace(/[^a-z0-9]/gi, "") ?? "") + randomString(11);
const fullAlias = `#${aliasName}:${client.getDomain()}`;
await client.createAlias(fullAlias, room.roomId)
await client.sendStateEvent(room.roomId, EventType.RoomCanonicalAlias, { alias: fullAlias }, "")
}

// it will take the new alias created previously or the existing one to make a link
const link = makeRoomPermalink(room.client, room.roomId);
setLinkSharingUrl(link);
} catch(err) {
console.error(err);
}
};

// Check if the current join rule is public or not
const isJoinRulePublic = () => {
const currentJoinRule: JoinRule = TchapRoomUtils.getRoomJoinRule(room) ?? JoinRule.Invite // if we dont receive the value we default to invite

return currentJoinRule === JoinRule.Public
}

// Set the new join rule (public or invite)
const _setJoinRules = async (joinRule: JoinRule) => {
try {
await room.client.sendStateEvent(room.roomId, EventType.RoomJoinRules, { join_rule: joinRule } as RoomJoinRulesEventContent, "");
setIsLinkSharingActivated(joinRule === JoinRule.Public);
} catch(err) {
console.error(err);
}
};

// Handler to listen on the switch change
const _onLinkSharingSwitchChange = async (checked: boolean) => {
let newJoinRule :JoinRule = checked ? JoinRule.Public : JoinRule.Invite;
setIsLinkSharingActivated(checked);

// if the link sharing is deactivated we also need to update the joinrule parent view to show the other options
if (!checked) {
await _setJoinRules(newJoinRule);
onBeforeChangeCallback(checked)
return;
}

// call parent join rule access, to confirm we want to change to public access and hide the other join options
onBeforeChangeCallback(checked, false, (actionConfirmed: boolean) => {
// create link if we activate the sharing, otherwise change nothing
if (actionConfirmed) {
_setUpRoomByLink();
_setJoinRules(JoinRule.Public)
} else {
// we revert because the action was not confirmed
setIsLinkSharingActivated(!checked);
}
})
};

return (
<div>
<LabelledToggleSwitch value={isLinkSharingActivated}
onChange={ _onLinkSharingSwitchChange }
label="Activate link access to this room"
caption="Les autres utilisateurs pourront rejoindre le salon a partir du lien suivant"
disabled={!TchapRoomUtils.isUserAdmin(room) || TchapRoomUtils.getTchapRoomType(room) === TchapRoomType.Forum}/>

{ isLinkSharingActivated ? linkSharingUrl : null}
</div>
)
}
55 changes: 46 additions & 9 deletions src/tchap/components/views/settings/TchapJoinRuleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React, { ReactNode } from "react";
import React, { ReactNode, useState } from "react";
import { IJoinRuleEventContent, JoinRule, RestrictedAllowType } from "matrix-js-sdk/src/@types/partials";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventType } from "matrix-js-sdk/src/@types/event";
Expand Down Expand Up @@ -43,6 +43,8 @@ import QuestionDialog from "matrix-react-sdk/src/components/views/dialogs/Questi

import TchapUIFeature from "../../../util/TchapUIFeature";
import { TchapRoomAccessRule, TchapIAccessRuleEventContent, TchapRoomAccessRulesEventId } from "../../../@types/tchap";
import TchapRoomLinkAccess from "../rooms/TchapRoomLinkAccess";
import TchapRoomUtils from "../../../util/TchapRoomUtils";

interface IProps {
room: Room;
Expand All @@ -56,6 +58,9 @@ interface IProps {
const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeChange, closeSettingsFn }: IProps) => {
const cli = room.client;

// Used to hide join rule option if link is activated
const [isShareLinkActivated, setIsLinkSharingActivated] = useState(false);

const roomSupportsRestricted = doesRoomVersionSupport(room.getVersion(), PreferredRoomVersions.RestrictedRooms);
const preferredRestrictionVersion =
!roomSupportsRestricted && promptUpgrade ? PreferredRoomVersions.RestrictedRooms : undefined;
Expand Down Expand Up @@ -419,15 +424,47 @@ const JoinRuleSettings = ({ room, promptUpgrade, aliasWarning, onError, beforeCh
setContent(newContent);
};

// This is a callback function used by the child link sharing component
// It will indicate wether or not to hide the joinrule options or not
const activateLinkSharingChange = async (checked: boolean, init: boolean, cb?: Function) => {
setIsLinkSharingActivated(checked);
// if its the initialisation phase we dont need to do anything more other than hide or not the join options
if (init) {
return;
}

// deactivating the share link
if (!checked) {
const currentJoinRule = TchapRoomUtils.getRoomJoinRule(room);
setContent(currentJoinRule ? { join_rule: JoinRule.Invite } : {} as IJoinRuleEventContent);
}

// getting the callback from the link sharing component
if (checked && cb && beforeChange) {
// showing the modal to confirm we want to turn the room to public
const resultConfirmed = await beforeChange(checked ? JoinRule.Public : JoinRule.Invite);
setIsLinkSharingActivated(resultConfirmed);
// if we cancel the modal, we need to revert back the switch and values
cb(resultConfirmed);
};
}

const renderLinkSharing = () => {
return <TchapRoomLinkAccess room={room} onBeforeChangeCallback={activateLinkSharingChange}></TchapRoomLinkAccess>
}

return (
<StyledRadioGroup
name="joinRule"
value={joinRule}
onChange={onChange}
definitions={definitions}
disabled={disabled}
className="mx_JoinRuleSettings_radioButton"
/>
<>
{!isShareLinkActivated && <StyledRadioGroup
name="joinRule"
value={joinRule}
onChange={onChange}
definitions={definitions}
disabled={disabled}
className="mx_JoinRuleSettings_radioButton"
/>}
{ renderLinkSharing() }
</>
);
};

Expand Down
25 changes: 22 additions & 3 deletions src/tchap/util/TchapRoomUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
* Tchap Room utils.
*/

import { Room } from "matrix-js-sdk/src/matrix";
import { EventTimeline, EventType, Room } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";

import { TchapRoomAccessRule, TchapRoomAccessRulesEventId, TchapRoomType } from "../@types/tchap";
import { JoinRule } from "matrix-js-sdk";

export default class TchapRoomUtils {
//inspired by https://github.com/tchapgouv/tchap-android/blob/develop/vector/src/main/java/fr/gouv/tchap/core/utils/RoomUtils.kt#L31
Expand Down Expand Up @@ -38,7 +39,7 @@ export default class TchapRoomUtils {
* @returns string that matches of one TchapRoomAccessRule //todo or null? or empty?
*/
static getTchapRoomAccessRule(room: Room): TchapRoomAccessRule {
return room.currentState.getStateEvents(TchapRoomAccessRulesEventId, "")?.getContent().rule;
return room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(TchapRoomAccessRulesEventId, "")?.getContent().rule;
}

/**
Expand All @@ -47,6 +48,24 @@ export default class TchapRoomUtils {
* @returns true if room is encrypted, false if not
*/
static isRoomEncrypted(roomId: string): boolean {
return MatrixClientPeg.get().isRoomEncrypted(roomId);
return !!MatrixClientPeg.get()?.getCrypto()?.isEncryptionEnabledInRoom(roomId);
}

/**
* Get if current is admin of the room
* @param room
* @returns
*/
static isUserAdmin(room: Room) : boolean {
const userId = room.client.getSafeUserId();
return room.getMember(userId)?.powerLevelNorm == 100
}

static getRoomJoinRule(room: Room): JoinRule|undefined {
return room
.getLiveTimeline()
.getState(EventTimeline.FORWARDS)
?.getStateEvents(EventType.RoomJoinRules, "")
?.getContent().join_rule
}
}
Binary file removed tchap-4.7.2-dev-upgrade2-20240916.tar.gz
Binary file not shown.

0 comments on commit ce3d1d0

Please sign in to comment.