-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial support for Hand Raise feature Signed-off-by: Milton Moura <[email protected]> * Refactored to use reaction and redaction events Signed-off-by: Milton Moura <[email protected]> * Replacing button svg with raised hand emoji Signed-off-by: Milton Moura <[email protected]> * SpotlightTile should not duplicate the raised hand Signed-off-by: Milton Moura <[email protected]> * Update src/room/useRaisedHands.tsx Element Call recently changed to AGPL-3.0 * Use relations to load existing reactions when joining the call Signed-off-by: Milton Moura <[email protected]> * Links to sha commit of matrix-js-sdk that exposes the call membership event id and refactors some async code Signed-off-by: Milton Moura <[email protected]> * Removing RaiseHand.svg * Check for reaction & redaction capabilities in widget mode Signed-off-by: Milton Moura <[email protected]> * Fix failing GridTile test Signed-off-by: Milton Moura <[email protected]> * Center align hand raise. * Add support for displaying the duration of a raised hand. * Add a sound for when a hand is raised. * Refactor raised hand indicator and add tests. * lint * Refactor into own files. * Redact the right thing. * Tidy up useEffect * Lint tests * Remove extra layer * Add better sound. (woosh) * Add a small mode for spotlight * Fix timestamp calculation on relaod. * Fix call border resizing video * lint * Fix and update tests * Allow timer to be configurable. * Add preferences tab for choosing to enable timer. * Drop border from raised hand icon * Handle cases when a new member event happens. * Prevent infinite loop * Major refactor to support various state problems. * Tidy up and finish test rewrites * Add some explanation comments. * Even more comments. * Use proper duration formatter * Remove rerender * Fix redactions not working because they pick up events in transit. * More tidying * Use deferred value * linting * Add tests for cases where we got a reaction from someone else. * Be even less brittle. * Transpose border to GridTile. * lint --------- Signed-off-by: Milton Moura <[email protected]> Co-authored-by: fkwp <[email protected]> Co-authored-by: Half-Shot <[email protected]> Co-authored-by: Will Hunt <[email protected]>
- Loading branch information
1 parent
f2ed07c
commit 1897210
Showing
24 changed files
with
1,149 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* | ||
Copyright 2024 New Vector Ltd. | ||
SPDX-License-Identifier: AGPL-3.0-only | ||
Please see LICENSE in the repository root for full details. | ||
*/ | ||
|
||
import { Button as CpdButton, Tooltip } from "@vector-im/compound-web"; | ||
import { | ||
ComponentPropsWithoutRef, | ||
FC, | ||
ReactNode, | ||
useCallback, | ||
useState, | ||
} from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { logger } from "matrix-js-sdk/src/logger"; | ||
import { EventType, RelationType } from "matrix-js-sdk/src/matrix"; | ||
import { MatrixClient } from "matrix-js-sdk/src/client"; | ||
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; | ||
|
||
import { useReactions } from "../useReactions"; | ||
import { useMatrixRTCSessionMemberships } from "../useMatrixRTCSessionMemberships"; | ||
|
||
interface InnerButtonProps extends ComponentPropsWithoutRef<"button"> { | ||
raised: boolean; | ||
} | ||
|
||
const InnerButton: FC<InnerButtonProps> = ({ raised, ...props }) => { | ||
const { t } = useTranslation(); | ||
|
||
return ( | ||
<Tooltip label={t("common.raise_hand")}> | ||
<CpdButton | ||
kind={raised ? "primary" : "secondary"} | ||
{...props} | ||
style={{ paddingLeft: 8, paddingRight: 8 }} | ||
> | ||
<p | ||
role="img" | ||
aria-hidden | ||
style={{ | ||
width: "30px", | ||
height: "0px", | ||
display: "inline-block", | ||
fontSize: "22px", | ||
}} | ||
> | ||
✋ | ||
</p> | ||
</CpdButton> | ||
</Tooltip> | ||
); | ||
}; | ||
|
||
interface RaisedHandToggleButtonProps { | ||
rtcSession: MatrixRTCSession; | ||
client: MatrixClient; | ||
} | ||
|
||
export function RaiseHandToggleButton({ | ||
client, | ||
rtcSession, | ||
}: RaisedHandToggleButtonProps): ReactNode { | ||
const { raisedHands, myReactionId } = useReactions(); | ||
const [busy, setBusy] = useState(false); | ||
const userId = client.getUserId()!; | ||
const isHandRaised = !!raisedHands[userId]; | ||
const memberships = useMatrixRTCSessionMemberships(rtcSession); | ||
|
||
const toggleRaisedHand = useCallback(() => { | ||
const raiseHand = async (): Promise<void> => { | ||
if (isHandRaised) { | ||
if (!myReactionId) { | ||
logger.warn(`Hand raised but no reaction event to redact!`); | ||
return; | ||
} | ||
try { | ||
setBusy(true); | ||
await client.redactEvent(rtcSession.room.roomId, myReactionId); | ||
logger.debug("Redacted raise hand event"); | ||
} catch (ex) { | ||
logger.error("Failed to redact reaction event", myReactionId, ex); | ||
} finally { | ||
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 { | ||
setBusy(true); | ||
const reaction = await client.sendEvent( | ||
rtcSession.room.roomId, | ||
EventType.Reaction, | ||
{ | ||
"m.relates_to": { | ||
rel_type: RelationType.Annotation, | ||
event_id: parentEventId, | ||
key: "🖐️", | ||
}, | ||
}, | ||
); | ||
logger.debug("Sent raise hand event", reaction.event_id); | ||
} catch (ex) { | ||
logger.error("Failed to send reaction event", ex); | ||
} finally { | ||
setBusy(false); | ||
} | ||
} | ||
}; | ||
|
||
void raiseHand(); | ||
}, [ | ||
client, | ||
isHandRaised, | ||
memberships, | ||
myReactionId, | ||
rtcSession.room.roomId, | ||
userId, | ||
]); | ||
|
||
return ( | ||
<InnerButton | ||
disabled={busy} | ||
onClick={toggleRaisedHand} | ||
raised={isHandRaised} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
.raisedHandWidget { | ||
display: flex; | ||
background-color: var(--cpd-color-bg-subtle-primary); | ||
border-radius: var(--cpd-radius-pill-effect); | ||
color: var(--cpd-color-icon-secondary); | ||
} | ||
|
||
.raisedHandWidget > p { | ||
padding: none; | ||
margin-top: auto; | ||
margin-bottom: auto; | ||
width: 4em; | ||
} | ||
|
||
.raisedHandWidgetLarge > p { | ||
padding: var(--cpd-space-2x); | ||
} | ||
|
||
.raisedHandLarge { | ||
margin: var(--cpd-space-2x); | ||
padding: var(--cpd-space-2x); | ||
padding-block: var(--cpd-space-2x); | ||
} | ||
|
||
.raisedHand { | ||
margin: var(--cpd-space-1x); | ||
color: var(--cpd-color-icon-secondary); | ||
background-color: var(--cpd-color-icon-secondary); | ||
display: flex; | ||
align-items: center; | ||
border-radius: var(--cpd-radius-pill-effect); | ||
user-select: none; | ||
overflow: hidden; | ||
box-shadow: var(--small-drop-shadow); | ||
box-sizing: border-box; | ||
max-inline-size: 100%; | ||
max-width: fit-content; | ||
} | ||
|
||
.raisedHand > span { | ||
width: var(--cpd-space-6x); | ||
height: var(--cpd-space-6x); | ||
display: inline-block; | ||
text-align: center; | ||
font-size: 16px; | ||
} | ||
|
||
.raisedHandLarge > span { | ||
width: var(--cpd-space-8x); | ||
height: var(--cpd-space-8x); | ||
font-size: 22px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
Copyright 2024 New Vector Ltd. | ||
SPDX-License-Identifier: AGPL-3.0-only | ||
Please see LICENSE in the repository root for full details. | ||
*/ | ||
|
||
import { describe, expect, test } from "vitest"; | ||
import { render, configure } from "@testing-library/react"; | ||
|
||
import { RaisedHandIndicator } from "./RaisedHandIndicator"; | ||
|
||
configure({ | ||
defaultHidden: true, | ||
}); | ||
|
||
describe("RaisedHandIndicator", () => { | ||
test("renders nothing when no hand has been raised", () => { | ||
const { container } = render(<RaisedHandIndicator />); | ||
expect(container.firstChild).toBeNull(); | ||
}); | ||
test("renders an indicator when a hand has been raised", () => { | ||
const dateTime = new Date(); | ||
const { container } = render( | ||
<RaisedHandIndicator raisedHandTime={dateTime} showTimer />, | ||
); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
test("renders an indicator when a hand has been raised with the expected time", () => { | ||
const dateTime = new Date(new Date().getTime() - 60000); | ||
const { container } = render( | ||
<RaisedHandIndicator raisedHandTime={dateTime} showTimer />, | ||
); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
test("renders a smaller indicator when minature is specified", () => { | ||
const dateTime = new Date(); | ||
const { container } = render( | ||
<RaisedHandIndicator raisedHandTime={dateTime} minature showTimer />, | ||
); | ||
expect(container.firstChild).toMatchSnapshot(); | ||
}); | ||
}); |
Oops, something went wrong.