element-web/src/hooks/usePermalink.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

183 lines
6.1 KiB
TypeScript
Raw Normal View History

2023-03-08 12:06:50 +00:00
/*
Copyright 2023 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.
*/
2023-03-22 12:27:24 +00:00
import { MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
2023-03-08 12:06:50 +00:00
import { ButtonEvent } from "../components/views/elements/AccessibleButton";
import { PillType } from "../components/views/elements/Pill";
import { parsePermalink } from "../utils/permalinks/Permalinks";
import dis from "../dispatcher/dispatcher";
import { Action } from "../dispatcher/actions";
2023-03-21 09:23:20 +00:00
import { PermalinkParts } from "../utils/permalinks/PermalinkConstructor";
import { _t } from "../languageHandler";
import { usePermalinkTargetRoom } from "./usePermalinkTargetRoom";
import { usePermalinkEvent } from "./usePermalinkEvent";
import { usePermalinkMember } from "./usePermalinkMember";
2023-03-08 12:06:50 +00:00
interface Args {
/** Room in which the permalink should be displayed. */
room?: Room;
/** When set forces the permalink type. */
type?: PillType;
/** Permalink URL. */
url?: string;
}
interface HookResult {
/**
* Room member of a user mention permalink.
* null for other links, if the profile was not found or not yet loaded.
* This can change, for instance, from null to a RoomMember after the profile lookup completed.
*/
member: RoomMember | null;
/**
* Displayable text of the permalink resource. Can for instance be a user or room name.
* null here means that there is nothing to display. Most likely if the URL was not a permalink.
*/
2023-03-08 12:06:50 +00:00
text: string | null;
/**
* Should be used for click actions on the permalink.
* In case of a user permalink, a view profile action is dispatched.
*/
onClick: (e: ButtonEvent) => void;
/**
* This can be for instance a user or room Id.
* null here means that the resource cannot be detected. Most likely if the URL was not a permalink.
*/
2023-03-08 12:06:50 +00:00
resourceId: string | null;
/**
* Target room of the permalink:
* For an @room mention, this is the room where the permalink should be displayed.
* For a room permalink, it is the room from the permalink.
* null for other links or if the room cannot be found.
*/
targetRoom: Room | null;
/**
* Type of the pill plus "space" for spaces.
* null here means that the type cannot be detected. Most likely if the URL was not a permalink.
*/
2023-03-08 12:06:50 +00:00
type: PillType | "space" | null;
2023-03-22 12:27:24 +00:00
/**
* Target event of the permalink.
* Null if unable to load the event.
*/
event: MatrixEvent | null;
2023-03-08 12:06:50 +00:00
}
/**
2023-03-21 09:23:20 +00:00
* Tries to determine the pill type.
*
* If forcedType is present it will be returned.
* If the parse result contains a room Id or alias and an event Id:
* - Type is EventInSameRoom if the permalink room Id or alias equals the parsed room Id or alias
* - Type is EventInOtherRoom if the permalink room Id or alias not equals the parsed room Id or alias
* If the parse result contains a primary entity Id it will try to detect the type from it.
* Otherwise returns null.
*
* @param forcedType - Forced pill type. Will be used if present and short-circuits all othe conditions.
* @param parseResult - Permalink parser result
* @param permalinkRoom - Room in which the permalink is displayed.
* @returns Pill type or null if unable to determine.
2023-03-08 12:06:50 +00:00
*/
2023-03-21 09:23:20 +00:00
const determineType = (
forcedType: PillType | undefined,
parseResult: PermalinkParts | null,
permalinkRoom: Room | undefined,
): PillType | null => {
if (forcedType) return forcedType;
if (parseResult?.roomIdOrAlias && parseResult?.eventId) {
if (parseResult.roomIdOrAlias === permalinkRoom?.roomId) {
return PillType.EventInSameRoom;
}
return PillType.EventInOtherRoom;
}
if (parseResult?.primaryEntityId) {
const prefix = parseResult.primaryEntityId[0] || "";
return (
{
"@": PillType.UserMention,
"#": PillType.RoomMention,
"!": PillType.RoomMention,
}[prefix] || null
);
}
return null;
};
2023-03-08 12:06:50 +00:00
2023-03-21 09:23:20 +00:00
/**
* Can be used to retrieve all information needed to display a permalink.
*/
export const usePermalink: (args: Args) => HookResult = ({
room: permalinkRoom,
type: forcedType,
url,
}): HookResult => {
2023-03-08 12:06:50 +00:00
let resourceId: string | null = null;
2023-03-21 09:23:20 +00:00
let parseResult: PermalinkParts | null = null;
2023-03-08 12:06:50 +00:00
if (url) {
2023-03-21 09:23:20 +00:00
parseResult = parsePermalink(url);
2023-03-08 12:06:50 +00:00
if (parseResult?.primaryEntityId) {
resourceId = parseResult.primaryEntityId;
}
}
2023-03-21 09:23:20 +00:00
const type = determineType(forcedType, parseResult, permalinkRoom);
const targetRoom = usePermalinkTargetRoom(type, parseResult, permalinkRoom);
const event = usePermalinkEvent(type, parseResult, targetRoom);
const member = usePermalinkMember(type, parseResult, targetRoom, event);
2023-03-08 12:06:50 +00:00
let onClick: (e: ButtonEvent) => void = () => {};
2023-03-08 12:06:50 +00:00
let text = resourceId;
2023-03-21 09:23:20 +00:00
if (type === PillType.AtRoomMention && permalinkRoom) {
2023-03-08 12:06:50 +00:00
text = "@room";
} else if (type === PillType.UserMention && member) {
text = member.name || resourceId;
onClick = (e: ButtonEvent): void => {
e.preventDefault();
2023-03-13 14:43:13 +00:00
e.stopPropagation();
2023-03-08 12:06:50 +00:00
dis.dispatch({
action: Action.ViewUser,
member: member,
});
};
} else if (type === PillType.RoomMention) {
if (targetRoom) {
text = targetRoom.name || resourceId;
}
2023-03-21 09:23:20 +00:00
} else if (type === PillType.EventInSameRoom) {
text = member?.name || _t("User");
} else if (type === PillType.EventInOtherRoom) {
text = targetRoom?.name || _t("Room");
2023-03-08 12:06:50 +00:00
}
return {
2023-03-22 12:27:24 +00:00
event,
member,
2023-03-08 12:06:50 +00:00
onClick,
resourceId,
targetRoom,
text,
2023-03-08 12:06:50 +00:00
type,
};
};