mirror of
https://github.com/vector-im/element-call.git
synced 2026-07-18 18:59:23 +00:00
Tidy up + add support for floating emoji.
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
.reactionPopupMenu {
|
.reactionPopupMenu {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 99;
|
|
||||||
background: var(--cpd-color-bg-canvas-default);
|
background: var(--cpd-color-bg-canvas-default);
|
||||||
top: -8em;
|
top: -8em;
|
||||||
border-radius: var(--cpd-space-4x);
|
border-radius: var(--cpd-space-4x);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Search,
|
Search,
|
||||||
Form,
|
Form,
|
||||||
} from "@vector-im/compound-web";
|
} from "@vector-im/compound-web";
|
||||||
|
import { ReactionIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import {
|
import {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
ComponentPropsWithoutRef,
|
ComponentPropsWithoutRef,
|
||||||
@@ -46,23 +47,11 @@ const InnerButton: FC<InnerButtonProps> = ({ raised, ...props }) => {
|
|||||||
return (
|
return (
|
||||||
<Tooltip label={t("action.send_reaction")}>
|
<Tooltip label={t("action.send_reaction")}>
|
||||||
<CpdButton
|
<CpdButton
|
||||||
|
iconOnly
|
||||||
kind={raised ? "primary" : "secondary"}
|
kind={raised ? "primary" : "secondary"}
|
||||||
{...props}
|
{...props}
|
||||||
style={{ paddingLeft: 8, paddingRight: 8 }}
|
Icon={ReactionIcon}
|
||||||
>
|
/>
|
||||||
<p
|
|
||||||
role="img"
|
|
||||||
aria-hidden
|
|
||||||
style={{
|
|
||||||
width: "30px",
|
|
||||||
height: "0px",
|
|
||||||
display: "inline-block",
|
|
||||||
fontSize: "22px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
✋
|
|
||||||
</p>
|
|
||||||
</CpdButton>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -101,14 +90,14 @@ export function ReactionPopupMenu({
|
|||||||
<CpdButton
|
<CpdButton
|
||||||
kind={isHandRaised ? "primary" : "secondary"}
|
kind={isHandRaised ? "primary" : "secondary"}
|
||||||
className={styles.reactionButton}
|
className={styles.reactionButton}
|
||||||
key={"raise-hand"}
|
key="raise-hand"
|
||||||
onClick={() => toggleRaisedHand()}
|
onClick={() => toggleRaisedHand()}
|
||||||
>
|
>
|
||||||
🖐️
|
🖐️
|
||||||
</CpdButton>
|
</CpdButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</section>
|
</section>
|
||||||
<div className={styles.verticalSeperator}></div>
|
<div className={styles.verticalSeperator} />
|
||||||
<section>
|
<section>
|
||||||
<Form.Root onSubmit={(e) => e.preventDefault()}>
|
<Form.Root onSubmit={(e) => e.preventDefault()}>
|
||||||
<Search
|
<Search
|
||||||
@@ -168,8 +157,7 @@ export function RaiseHandToggleButton({
|
|||||||
const parentEventId = myMembership.eventId;
|
const parentEventId = myMembership.eventId;
|
||||||
try {
|
try {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
// XXX: Trying to send a unspec'd event seems to miss the 3rd overload, need to come back to this.
|
// @ts-expect-error Trying to send a unspec'd event seems to miss the 3rd overload, need to come back to this.
|
||||||
// @ts-expect-error
|
|
||||||
await client.sendEvent(
|
await client.sendEvent(
|
||||||
rtcSession.room.roomId,
|
rtcSession.room.roomId,
|
||||||
null,
|
null,
|
||||||
@@ -190,7 +178,7 @@ export function RaiseHandToggleButton({
|
|||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[memberships, client],
|
[memberships, client, userId, rtcSession],
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleRaisedHand = useCallback(() => {
|
const toggleRaisedHand = useCallback(() => {
|
||||||
@@ -253,13 +241,13 @@ export function RaiseHandToggleButton({
|
|||||||
<InnerButton
|
<InnerButton
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
onClick={() => setShowReactionsMenu((show) => !show)}
|
onClick={() => setShowReactionsMenu((show) => !show)}
|
||||||
raised={isHandRaised}
|
raised={isHandRaised || showReactionsMenu}
|
||||||
/>
|
/>
|
||||||
{showReactionsMenu && (
|
{showReactionsMenu && (
|
||||||
<ReactionPopupMenu
|
<ReactionPopupMenu
|
||||||
isHandRaised={isHandRaised}
|
isHandRaised={isHandRaised}
|
||||||
canReact={canReact}
|
canReact={canReact}
|
||||||
sendRelation={sendRelation}
|
sendRelation={(reaction) => void sendRelation(reaction)}
|
||||||
toggleRaisedHand={toggleRaisedHand}
|
toggleRaisedHand={toggleRaisedHand}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,11 +6,9 @@ Please see LICENSE in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
import classNames from "classnames";
|
|
||||||
import "@formatjs/intl-durationformat/polyfill";
|
import "@formatjs/intl-durationformat/polyfill";
|
||||||
import { DurationFormat } from "@formatjs/intl-durationformat";
|
import { DurationFormat } from "@formatjs/intl-durationformat";
|
||||||
|
|
||||||
import styles from "./RaisedHandIndicator.module.css";
|
|
||||||
import { ReactionIndicator } from "./ReactionIndicator";
|
import { ReactionIndicator } from "./ReactionIndicator";
|
||||||
|
|
||||||
const durationFormatter = new DurationFormat(undefined, {
|
const durationFormatter = new DurationFormat(undefined, {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { PropsWithChildren, ReactNode } from "react";
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import "@formatjs/intl-durationformat/polyfill";
|
import "@formatjs/intl-durationformat/polyfill";
|
||||||
|
|
||||||
import styles from "./RaisedHandIndicator.module.css";
|
import styles from "./ReactionIndicator.module.css";
|
||||||
|
|
||||||
export function ReactionIndicator({
|
export function ReactionIndicator({
|
||||||
emoji,
|
emoji,
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RelationType } from "matrix-js-sdk/src/types";
|
||||||
|
|
||||||
import catSoundOgg from "../sound/reactions/cat.mp3?url";
|
import catSoundOgg from "../sound/reactions/cat.mp3?url";
|
||||||
import catSoundMp3 from "../sound/reactions/cat.mp3?url";
|
import catSoundMp3 from "../sound/reactions/cat.mp3?url";
|
||||||
import cricketsSoundOgg from "../sound/reactions/crickets.mp3?url";
|
import cricketsSoundOgg from "../sound/reactions/crickets.mp3?url";
|
||||||
@@ -8,7 +17,7 @@ import genericSoundOgg from "../sound/reactions/generic.mp3?url";
|
|||||||
import genericSoundMp3 from "../sound/reactions/generic.mp3?url";
|
import genericSoundMp3 from "../sound/reactions/generic.mp3?url";
|
||||||
import lightbulbSoundOgg from "../sound/reactions/lightbulb.mp3?url";
|
import lightbulbSoundOgg from "../sound/reactions/lightbulb.mp3?url";
|
||||||
import lightbulbSoundMp3 from "../sound/reactions/lightbulb.mp3?url";
|
import lightbulbSoundMp3 from "../sound/reactions/lightbulb.mp3?url";
|
||||||
import { RelationType } from "matrix-js-sdk/src/types";
|
|
||||||
|
|
||||||
export interface ReactionOption {
|
export interface ReactionOption {
|
||||||
emoji: string;
|
emoji: string;
|
||||||
|
|||||||
@@ -144,3 +144,27 @@ Please see LICENSE in the repository root for full details.
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floatingReaction {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
font-size: 32pt;
|
||||||
|
animation-duration: 5s;
|
||||||
|
animation-name: reaction-up;
|
||||||
|
bottom: 0;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes reaction-up {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
translate: 100vw 0;
|
||||||
|
scale: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
translate: 100vw -100vh;
|
||||||
|
scale: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -188,6 +188,16 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
[reactions],
|
[reactions],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const reactionsIcons = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.entries(reactions).map(([sender, { emoji }]) => ({
|
||||||
|
sender,
|
||||||
|
emoji,
|
||||||
|
startX: -Math.ceil(Math.random() * 50) - 25,
|
||||||
|
})),
|
||||||
|
[reactions],
|
||||||
|
);
|
||||||
|
|
||||||
useWakeLock();
|
useWakeLock();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -651,6 +661,15 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
</audio>
|
</audio>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
{reactionsIcons.map(({ sender, emoji, startX }) => (
|
||||||
|
<span
|
||||||
|
style={{ left: `${startX}vw` }}
|
||||||
|
className={styles.floatingReaction}
|
||||||
|
key={sender}
|
||||||
|
>
|
||||||
|
{emoji}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
{footer}
|
{footer}
|
||||||
{!noControls && <RageshakeRequestModal {...rageshakeRequestModalProps} />}
|
{!noControls && <RageshakeRequestModal {...rageshakeRequestModalProps} />}
|
||||||
<SettingsModal
|
<SettingsModal
|
||||||
|
|||||||
Reference in New Issue
Block a user