diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json
index 69a1d105..22bfe646 100644
--- a/public/locales/en-GB/app.json
+++ b/public/locales/en-GB/app.json
@@ -4,11 +4,14 @@
},
"action": {
"close": "Close",
+ "close_search": "Close search",
"copy_link": "Copy link",
"edit": "Edit",
"go": "Go",
"invite": "Invite",
"no": "No",
+ "open_search": "Open search",
+ "pick_reaction": "Pick reaction",
"raise_hand_or_send_reaction": "Raise hand or send reaction",
"register": "Register",
"remove": "Remove",
@@ -58,6 +61,7 @@
"preferences": "Preferences",
"profile": "Profile",
"raise_hand": "Raise hand",
+ "search": "Search",
"settings": "Settings",
"unencrypted": "Not encrypted",
"username": "Username",
@@ -128,6 +132,7 @@
"rageshake_sending": "Sending…",
"rageshake_sending_logs": "Sending debug logs…",
"rageshake_sent": "Thanks!",
+ "reaction_search": "Search reactions…",
"recaptcha_caption": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy2> and <6>Terms of Service6> apply.<9>9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)12>",
"recaptcha_dismissed": "Recaptcha dismissed",
"recaptcha_not_loaded": "Recaptcha not loaded",
diff --git a/src/button/ReactionToggleButton.module.css b/src/button/ReactionToggleButton.module.css
index 1919dd18..acb8bb25 100644
--- a/src/button/ReactionToggleButton.module.css
+++ b/src/button/ReactionToggleButton.module.css
@@ -64,3 +64,19 @@
.searchForm > label {
flex: auto;
}
+
+.alert {
+ margin-bottom: var(--cpd-space-3x);
+ animation: grow-in 200ms;
+ height: 2.5em;
+}
+
+@keyframes grow-in {
+ from {
+ height: 0;
+ }
+
+ to {
+ height: 2.5em;
+ }
+}
diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx
index 0b07f665..44ab7218 100644
--- a/src/button/ReactionToggleButton.tsx
+++ b/src/button/ReactionToggleButton.tsx
@@ -11,6 +11,7 @@ import {
Separator,
Search,
Form,
+ Alert,
} from "@vector-im/compound-web";
import {
SearchIcon,
@@ -25,6 +26,7 @@ import {
KeyboardEventHandler,
ReactNode,
useCallback,
+ useEffect,
useMemo,
useState,
} from "react";
@@ -72,9 +74,11 @@ export function ReactionPopupMenu({
toggleRaisedHand,
isHandRaised,
canReact,
+ errorText,
}: {
sendReaction: (reaction: ReactionOption) => void;
toggleRaisedHand: () => void;
+ errorText?: string;
isHandRaised: boolean;
canReact: boolean;
}): ReactNode {
@@ -119,80 +123,91 @@ export function ReactionPopupMenu({
);
const label = isHandRaised ? t("common.raise_hand") : t("common.lower_hand");
return (
-
-
-
- toggleRaisedHand()}
- iconOnly
- Icon={RaisedHandSolidIcon}
- />
-
-
-
-
- {isSearching ? (
- <>
-
-
- setIsSearching(false)}
- />
-
-
- >
- ) : null}
-
+ ) : null}
+
+ >
);
}
@@ -205,24 +220,30 @@ export function ReactionToggleButton({
client,
rtcSession,
}: ReactionToggleButtonProps): ReactNode {
+ const { t } = useTranslation();
const { raisedHands, lowerHand, reactions } = useReactions();
const [busy, setBusy] = useState(false);
const userId = client.getUserId()!;
const isHandRaised = !!raisedHands[userId];
const memberships = useMatrixRTCSessionMemberships(rtcSession);
const [showReactionsMenu, setShowReactionsMenu] = useState(false);
+ const [errorText, setErrorText] = useState();
+
+ useEffect(() => {
+ // Clear whenever the reactions menu state changes.
+ setErrorText(undefined);
+ }, [showReactionsMenu]);
const canReact = !reactions[userId];
const sendRelation = useCallback(
async (reaction: ReactionOption) => {
- const myMembership = memberships.find((m) => m.sender === userId);
- if (!myMembership?.eventId) {
- logger.error("Cannot find own membership event");
- return;
- }
- const parentEventId = myMembership.eventId;
try {
+ const myMembership = memberships.find((m) => m.sender === userId);
+ if (!myMembership?.eventId) {
+ throw new Error("Cannot find own membership event");
+ }
+ const parentEventId = myMembership.eventId;
setBusy(true);
await client.sendEvent(
rtcSession.room.roomId,
@@ -236,12 +257,14 @@ export function ReactionToggleButton({
name: reaction.name,
},
);
+ setErrorText(undefined);
+ setShowReactionsMenu(false);
// Do NOT close the menu after this.
} catch (ex) {
+ setErrorText(ex instanceof Error ? ex.message : "Unknown error");
logger.error("Failed to send reaction", ex);
} finally {
setBusy(false);
- setShowReactionsMenu(false);
}
},
[memberships, client, userId, rtcSession],
@@ -258,13 +281,12 @@ export function ReactionToggleButton({
setBusy(false);
}
} else {
- const myMembership = memberships.find((m) => m.sender === userId);
- if (!myMembership?.eventId) {
- logger.error("Cannot find own membership event");
- return;
- }
- const parentEventId = myMembership.eventId;
try {
+ const myMembership = memberships.find((m) => m.sender === userId);
+ if (!myMembership?.eventId) {
+ throw new Error("Cannot find own membership event");
+ }
+ const parentEventId = myMembership.eventId;
setBusy(true);
const reaction = await client.sendEvent(
rtcSession.room.roomId,
@@ -278,11 +300,13 @@ export function ReactionToggleButton({
},
);
logger.debug("Sent raise hand event", reaction.event_id);
+ setErrorText(undefined);
+ setShowReactionsMenu(false);
} catch (ex) {
- logger.error("Failed to send reaction event", ex);
+ setErrorText(ex instanceof Error ? ex.message : "Unknown error");
+ logger.error("Failed to raise hand", ex);
} finally {
setBusy(false);
- setShowReactionsMenu(false);
}
}
};
@@ -307,12 +331,13 @@ export function ReactionToggleButton({
/>
setShowReactionsMenu(false)}
>
void sendRelation(reaction)}