Skip to content

Commit

Permalink
Merge pull request #45 from superhero-com/fix/disabled-message
Browse files Browse the repository at this point in the history
feat: adds thresholds to preview screens & chat bar
  • Loading branch information
kenodressel authored Jan 22, 2024
2 parents 96223fb + 0f19bb7 commit ab8f4f4
Show file tree
Hide file tree
Showing 12 changed files with 1,635 additions and 42 deletions.
2 changes: 2 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"src/components/views/auth/AuthFooter.tsx": "src/components/views/auth/VectorAuthFooter.tsx",
"src/components/views/auth/AuthHeaderLogo.tsx": "src/components/views/auth/VectorAuthHeaderLogo.tsx",
"src/components/views/auth/AuthPage.tsx": "src/components/views/auth/VectorAuthPage.tsx",
"src/components/views/rooms/MessageComposer.tsx": "src/components/views/rooms/MessageComposer.tsx",
"src/components/views/rooms/RoomTile.tsx": "src/components/views/rooms/RoomTile.tsx",
"src/components/views/rooms/RoomPreviewBar.tsx": "src/components/views/rooms/RoomPreviewBar.tsx",
"src/components/views/rooms/NewRoomIntro.tsx": "src/components/views/rooms/NewRoomIntro.tsx",
"src/components/views/elements/RoomName.tsx": "src/components/views/elements/RoomName.tsx",
"src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx": "src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx",
Expand Down
3 changes: 2 additions & 1 deletion config.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@
"participant_limit": 8,
"brand": "Element Call"
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
"community_bot_user_id": "@communitybot:superhero.com"
}
15 changes: 15 additions & 0 deletions src/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import { atomWithStorage } from "jotai/utils";

type TokenThreshold = {
threshold: string;
symbol: string;
};

export type BareUser = {
userId: string;
rawDisplayName: string;
};

export const verifiedAccountsAtom = atomWithStorage<Record<string, string>>("VERIFIED_ACCOUNTS", {});
export const minimumTokenThresholdAtom = atomWithStorage<Record<string, TokenThreshold>>("TOKEN_THRESHOLD", {});
export const communityBotAtom = atomWithStorage<BareUser>("COMMUNITY_BOT", {
userId: "",
rawDisplayName: "",
});
20 changes: 20 additions & 0 deletions src/components/views/elements/CommunityRoomPeekMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useAtom } from "jotai";
import React, { ReactElement } from "react";

import { minimumTokenThresholdAtom } from "../../../atoms";
import { _t } from "../../../languageHandler";
import { cleanRoomName } from "../../../hooks/useVerifiedRoom";

export function CommunityRoomPeekMessage({ roomName }: { roomName: string }): ReactElement {
const [allTokens] = useAtom(minimumTokenThresholdAtom);
const cleanedRoomName = cleanRoomName(roomName);

const tokenThreshold = allTokens[cleanedRoomName];

return (
<h3>
{_t("room|no_peek_join_prompt_community", { roomName: cleanedRoomName })}{" "}
{tokenThreshold ? _t("room|no_peek_join_prompt_community_threshold", tokenThreshold) : ""}
</h3>
);
}
44 changes: 44 additions & 0 deletions src/components/views/elements/DisabledMessageField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useAtom } from "jotai";
import React from "react";
import { Room } from "matrix-js-sdk/src/matrix";

import { minimumTokenThresholdAtom } from "../../../atoms";
import { _t } from "../../../languageHandler";
import { useVerifiedRoom } from "../../../hooks/useVerifiedRoom";
import { MessageCommunityBotButton } from "./MessageButton";

export function DisabledMessageField({ room }: { room: Room }): JSX.Element {
const [allTokens] = useAtom(minimumTokenThresholdAtom);
const { isTokenGatedRoom, isCommunityRoom } = useVerifiedRoom(room);

let tokenThreshold = allTokens[room.name];
if (!tokenThreshold) {
const tokenName = room.name.match(/\[TG] (.*) \(ct_.*\)/)?.[1];
if (isTokenGatedRoom && tokenName) {
tokenThreshold = {
threshold: "1",
symbol: tokenName,
};
}
}

if (tokenThreshold) {
return (
<div key="controls_error" className="mx_MessageComposer_noperm_error">
{_t("composer|no_perms_token_notice", tokenThreshold)}
{isCommunityRoom ? (
<>
<span style={{ marginLeft: "1rem", display: "block" }} />
<MessageCommunityBotButton text="Get room tokens" />
</>
) : null}
</div>
);
} else {
return (
<div key="controls_error" className="mx_MessageComposer_noperm_error">
{_t("composer|no_perms_notice")}
</div>
);
}
}
57 changes: 57 additions & 0 deletions src/components/views/elements/MessageButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useContext, useState } from "react";
import MatrixClientContext from "matrix-react-sdk/src/contexts/MatrixClientContext";
import AccessibleButton from "matrix-react-sdk/src/components/views/elements/AccessibleButton";
import { MatrixClient, RoomMember, User } from "matrix-js-sdk/src/matrix";
import { DirectoryMember, startDmOnFirstMessage } from "matrix-react-sdk/src/utils/direct-messages";
import { useAtom } from "jotai";

import { Member } from "../right_panel/UserInfo";
import { Icon as SendMessage } from "../../../../res/themes/superhero/img/icons/send.svg";
import { BareUser, communityBotAtom } from "../../../atoms";

/**
* Converts the member to a DirectoryMember and starts a DM with them.
*/
async function openDmForUser(matrixClient: MatrixClient, user: Member | BareUser): Promise<void> {
const avatarUrl = user instanceof User ? user.avatarUrl : user instanceof RoomMember ? user.getMxcAvatarUrl() : "";
const startDmUser = new DirectoryMember({
user_id: user.userId,
display_name: user.rawDisplayName,
avatar_url: avatarUrl,
});
await startDmOnFirstMessage(matrixClient, [startDmUser]);
}

export const MessageButton = ({
member,
text = "Send Message",
}: {
member: Member | BareUser;
text?: string;
}): JSX.Element => {
const cli = useContext(MatrixClientContext);
const [busy, setBusy] = useState(false);

return (
<AccessibleButton
kind="primary"
onClick={async (): Promise<void> => {
if (busy) return;
setBusy(true);
await openDmForUser(cli, member);
setBusy(false);
}}
className="mx_UserInfo_field"
disabled={busy}
>
<SendMessage width="16px" height="16px" />
<span style={{ marginLeft: "5px" }}>{text}</span>
</AccessibleButton>
);
};

export const MessageCommunityBotButton = ({ text = "Send Message" }: { text?: string }): JSX.Element => {
const [communityBot] = useAtom(communityBotAtom);

return <MessageButton member={communityBot} text={text} />;
};
38 changes: 1 addition & 37 deletions src/components/views/right_panel/UserInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,12 @@ import { IRightPanelCardState } from "matrix-react-sdk/src/stores/right-panel/Ri
import UserIdentifierCustomisations from "matrix-react-sdk/src/customisations/UserIdentifier";
import PosthogTrackers from "matrix-react-sdk/src/PosthogTrackers";
import { ViewRoomPayload } from "matrix-react-sdk/src/dispatcher/payloads/ViewRoomPayload";
import { DirectoryMember, startDmOnFirstMessage } from "matrix-react-sdk/src/utils/direct-messages";
import { SdkContextClass } from "matrix-react-sdk/src/contexts/SDKContext";
import { asyncSome } from "matrix-react-sdk/src/utils/arrays";
import UIStore from "matrix-react-sdk/src/stores/UIStore";

import { UserVerifiedBadge } from "../elements/UserVerifiedBadge";
import { Icon as SendMessage } from "../../../../res/themes/superhero/img/icons/send.svg";
import { MessageButton } from "../elements/MessageButton";

export interface IDevice extends Device {
ambiguous?: boolean;
Expand Down Expand Up @@ -133,19 +132,6 @@ export const getE2EStatus = async (
return anyDeviceUnverified ? E2EStatus.Warning : E2EStatus.Verified;
};

/**
* Converts the member to a DirectoryMember and starts a DM with them.
*/
async function openDmForUser(matrixClient: MatrixClient, user: Member): Promise<void> {
const avatarUrl = user instanceof User ? user.avatarUrl : user.getMxcAvatarUrl();
const startDmUser = new DirectoryMember({
user_id: user.userId,
display_name: user.rawDisplayName,
avatar_url: avatarUrl,
});
await startDmOnFirstMessage(matrixClient, [startDmUser]);
}

type SetUpdating = (updating: boolean) => void;

function useHasCrossSigningKeys(
Expand Down Expand Up @@ -359,28 +345,6 @@ function DevicesSection({
);
}

const MessageButton = ({ member }: { member: Member }): JSX.Element => {
const cli = useContext(MatrixClientContext);
const [busy, setBusy] = useState(false);

return (
<AccessibleButton
kind="primary"
onClick={async (): Promise<void> => {
if (busy) return;
setBusy(true);
await openDmForUser(cli, member);
setBusy(false);
}}
className="mx_UserInfo_field"
disabled={busy}
>
<SendMessage width="16px" height="16px" />
<span style={{ marginLeft: "5px" }}>Send Message</span>
</AccessibleButton>
);
};

export const UserOptionsSection: React.FC<{
member: Member;
isIgnored: boolean;
Expand Down
Loading

0 comments on commit ab8f4f4

Please sign in to comment.