diff --git a/config/nginx.conf b/config/nginx.conf index f3f8140e..e6f6e6be 100644 --- a/config/nginx.conf +++ b/config/nginx.conf @@ -8,7 +8,7 @@ server { location / { try_files $uri $uri/ /index.html; - add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Cache-Control "public, max-age=30, stale-while-revalidate=30"; } # assets can be cached because they have hashed filenames diff --git a/locales/de/app.json b/locales/de/app.json index 744fa792..b73d8c3c 100644 --- a/locales/de/app.json +++ b/locales/de/app.json @@ -163,8 +163,8 @@ "preferences_tab": { "reactions_play_sound_description": "Einen Soundeffekt abspielen, wenn jemand eine Reaktion sendet", "reactions_play_sound_label": "Reaktionstöne abspielen", - "reactions_show_description": "Reaktionen anzeigen", - "reactions_show_label": "Zeige eine Animation, wenn jemand eine Reaktion sendet.", + "reactions_show_description": "Zeige eine Animation, wenn jemand eine Reaktion sendet.", + "reactions_show_label": "Reaktionen anzeigen", "reactions_title": "Reaktionen" }, "preferences_tab_body": "Hier können zusätzliche Optionen für individuelle Anforderungen eingestellt werden", diff --git a/locales/en-GB/app.json b/locales/en-GB/app.json index d18bc3a5..7f142953 100644 --- a/locales/en-GB/app.json +++ b/locales/en-GB/app.json @@ -4,19 +4,19 @@ }, "action": { "close": "Close", - "close_search": "Close search", "copy_link": "Copy link", "edit": "Edit", "go": "Go", "invite": "Invite", "lower_hand": "Lower hand ({{keyboardShortcut}})", "no": "No", - "open_search": "Open search", "pick_reaction": "Pick reaction", "raise_hand": "Raise hand ({{keyboardShortcut}})", "raise_hand_or_send_reaction": "Raise hand or send reaction", "register": "Register", "remove": "Remove", + "show_less": "Show less", + "show_more": "Show more", "sign_in": "Sign in", "sign_out": "Sign out", "submit": "Submit", @@ -62,7 +62,6 @@ "preferences": "Preferences", "profile": "Profile", "reaction": "Reaction", - "search": "Search", "settings": "Settings", "something_went_wrong": "Something went wrong", "unencrypted": "Not encrypted", @@ -128,7 +127,6 @@ "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 Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>End User Licensing Agreement (EULA)", "recaptcha_dismissed": "Recaptcha dismissed", "recaptcha_not_loaded": "Recaptcha not loaded", @@ -163,8 +161,8 @@ "preferences_tab": { "reactions_play_sound_description": "Play a sound effect when anyone sends a reaction into a call.", "reactions_play_sound_label": "Play reaction sounds", - "reactions_show_description": "Show reactions", - "reactions_show_label": "Show an animation when anyone sends a reaction.", + "reactions_show_description": "Show an animation when anyone sends a reaction.", + "reactions_show_label": "Show reactions", "reactions_title": "Reactions" }, "preferences_tab_body": "Here you can configure extra options for an improved experience", diff --git a/public/index.html b/public/index.html index df1edfd9..e14d79be 100644 --- a/public/index.html +++ b/public/index.html @@ -3,6 +3,7 @@ + div > div { - padding-inline: var(--cpd-space-6x) !important; - padding-block: var(--cpd-space-6x) var(--cpd-space-8x) !important; +div.reactionPopupMenuRoot.reactionPopupMenuModal { + --overlay-top: 82vh; + width: fit-content; } -.reactionPopupMenu menu { - margin: 0; - padding: 0; - display: flex; - flex-wrap: wrap; - gap: var(--cpd-separator-spacing); +div.reactionPopupMenuRoot { + /* Center the drawer */ + --inset-inline: 30em; +} + +.reactionPopupMenuRoot > div { + width: fit-content; + max-width: 100vw; +} + +div.reactionPopupMenuRoot.reactionPopupMenuModal > div > div { + padding-inline: var(--cpd-space-6x); + padding-block: var(--cpd-space-6x); } .reactionPopupMenu section { height: fit-content; - margin-top: auto; - margin-bottom: auto; + flex: 1; + max-width: fit-content; } -.reactionPopupMenuItem { - list-style: none; +.reactionPopupMenu section.reactionsMenuSection { + margin: auto 0; + flex: auto; } .reactionsMenu { - min-height: 3em; + margin: 0; + padding: 0; + flex-grow: 1; + gap: var(--reaction-button-gap); + /* Height of 3 rows plus padding. */ + max-height: calc( + ((var(--reaction-button-fontsize) + var(--cpd-separator-spacing)) * 2) * 3 + ); + max-width: calc( + ((var(--reaction-button-fontsize) + var(--cpd-separator-spacing)) * 2) * 5 + ); + overflow-x: hidden; + overflow-y: auto; + list-style: none; + flex-wrap: wrap; + display: flex; + flex-wrap: wrap; + flex-direction: row; + justify-content: start; + align-items: auto; + align-content: start; + width: fit-content; +} + +.reactionsMenu > * { + flex: 0 0 auto; } .reactionButton { - padding: 1em; - font-size: 1.6em; - width: 1.4em; - height: 1.4em; + padding: var(--reaction-button-padding); border-radius: var(--cpd-radius-pill-effect); -} - -@media (max-width: 800px) { - .reactionButton { - padding: 1em; - font-size: 1em; - width: 1em; - height: 1em; - min-block-size: unset; - } + font-size: var(--reaction-button-fontsize); + min-block-size: unset; + border: none; + aspect-ratio: 1 / 1; + height: 100%; } .verticalSeperator { background-color: var(--cpd-color-gray-800); - width: 1px; + width: 2px; height: auto; margin-left: var(--cpd-separator-spacing); margin-right: var(--cpd-separator-spacing); } -.searchForm { - display: flex; - flex-direction: row; - gap: var(--cpd-separator-spacing); - margin-bottom: var(--cpd-space-3x); -} - -.searchForm > label { - flex: auto; -} - .alert { margin-bottom: var(--cpd-space-3x); animation: grow-in 200ms; diff --git a/src/button/ReactionToggleButton.test.tsx b/src/button/ReactionToggleButton.test.tsx index cab8a545..c48a6b4f 100644 --- a/src/button/ReactionToggleButton.test.tsx +++ b/src/button/ReactionToggleButton.test.tsx @@ -5,8 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { fireEvent, render } from "@testing-library/react"; -import { act } from "react"; +import { render } from "@testing-library/react"; import { expect, test } from "vitest"; import { TooltipProvider } from "@vector-im/compound-web"; import { userEvent } from "@testing-library/user-event"; @@ -116,7 +115,7 @@ test("Can react with emoji", async () => { ]); }); -test("Can search for and send emoji", async () => { +test("Can fully expand emoji picker", async () => { const user = userEvent.setup(); const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); @@ -124,9 +123,7 @@ test("Can search for and send emoji", async () => { , ); await user.click(getByLabelText("action.raise_hand_or_send_reaction")); - await user.click(getByLabelText("action.open_search")); - // Search should autofocus. - await user.keyboard("crickets"); + await user.click(getByLabelText("action.show_more")); expect(container).toMatchSnapshot(); await user.click(getByText("🦗")); @@ -146,39 +143,7 @@ test("Can search for and send emoji", async () => { ]); }); -test("Can search for and send emoji with the keyboard", async () => { - const user = userEvent.setup(); - const room = new MockRoom(memberUserIdAlice); - const rtcSession = new MockRTCSession(room, membership); - const { getByLabelText, getByPlaceholderText, container } = render( - , - ); - await user.click(getByLabelText("action.raise_hand_or_send_reaction")); - await user.click(getByLabelText("action.open_search")); - const searchField = getByPlaceholderText("reaction_search"); - // Search should autofocus. - await user.keyboard("crickets"); - expect(container).toMatchSnapshot(); - act(() => { - fireEvent.keyDown(searchField, { key: "Enter" }); - }); - expect(room.testSentEvents).toEqual([ - [ - undefined, - ElementCallReactionEventType, - { - "m.relates_to": { - event_id: memberEventAlice, - rel_type: "m.reference", - }, - name: "crickets", - emoji: "🦗", - }, - ], - ]); -}); - -test("Can close search", async () => { +test("Can close reaction dialog", async () => { const user = userEvent.setup(); const room = new MockRoom(memberUserIdAlice); const rtcSession = new MockRTCSession(room, membership); @@ -186,23 +151,7 @@ test("Can close search", async () => { , ); await user.click(getByLabelText("action.raise_hand_or_send_reaction")); - await user.click(getByLabelText("action.open_search")); - await user.click(getByLabelText("action.close_search")); - expect(container).toMatchSnapshot(); -}); - -test("Can close search with the escape key", async () => { - const user = userEvent.setup(); - const room = new MockRoom(memberUserIdAlice); - const rtcSession = new MockRTCSession(room, membership); - const { getByLabelText, container, getByPlaceholderText } = render( - , - ); - await user.click(getByLabelText("action.raise_hand_or_send_reaction")); - await user.click(getByLabelText("action.open_search")); - const searchField = getByPlaceholderText("reaction_search"); - act(() => { - fireEvent.keyDown(searchField, { key: "Escape" }); - }); + await user.click(getByLabelText("action.show_more")); + await user.click(getByLabelText("action.show_less")); expect(container).toMatchSnapshot(); }); diff --git a/src/button/ReactionToggleButton.tsx b/src/button/ReactionToggleButton.tsx index 7dc6f01a..9aef9bba 100644 --- a/src/button/ReactionToggleButton.tsx +++ b/src/button/ReactionToggleButton.tsx @@ -5,24 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ +import { Button as CpdButton, Tooltip, Alert } from "@vector-im/compound-web"; import { - Button as CpdButton, - Tooltip, - Search, - Form, - Alert, -} from "@vector-im/compound-web"; -import { - SearchIcon, - CloseIcon, RaisedHandSolidIcon, ReactionIcon, + ChevronDownIcon, + ChevronUpIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; import { - ChangeEventHandler, ComponentPropsWithoutRef, FC, - KeyboardEventHandler, ReactNode, useCallback, useEffect, @@ -76,43 +68,11 @@ export function ReactionPopupMenu({ canReact: boolean; }): ReactNode { const { t } = useTranslation(); - const [searchText, setSearchText] = useState(""); - const [isSearching, setIsSearching] = useState(false); - const onSearch = useCallback>((ev) => { - ev.preventDefault(); - setSearchText(ev.target.value.trim().toLocaleLowerCase()); - }, []); + const [isFullyExpanded, setExpanded] = useState(false); const filteredReactionSet = useMemo( - () => - ReactionSet.filter( - (reaction) => - !isSearching || - (!!searchText && - (reaction.name.startsWith(searchText) || - reaction.alias?.some((a) => a.startsWith(searchText)))), - ).slice(0, ReactionsRowSize), - [searchText, isSearching], - ); - - const onSearchKeyDown = useCallback>( - (ev) => { - if (ev.key === "Enter") { - ev.preventDefault(); - if (!canReact) { - return; - } - if (filteredReactionSet.length !== 1) { - return; - } - sendReaction(filteredReactionSet[0]); - setIsSearching(false); - } else if (ev.key === "Escape") { - ev.preventDefault(); - setIsSearching(false); - } - }, - [sendReaction, filteredReactionSet, canReact, setIsSearching], + () => (isFullyExpanded ? ReactionSet : ReactionSet.slice(0, 5)), + [isFullyExpanded], ); const label = isHandRaised ? t("action.lower_hand", { keyboardShortcut: "H" }) @@ -143,36 +103,15 @@ export function ReactionPopupMenu({
-
- {isSearching ? ( - <> - - - setIsSearching(false)} - /> - - - ) : null} - +
+ {filteredReactionSet.map((reaction, index) => ( -
  • - {/* Show the keyboard key assigned to the reaction */} +
  • - {!isSearching ? ( -
    -
  • - - setIsSearching(true)} - /> - -
  • -
    - ) : null} +
    + + setExpanded(!isFullyExpanded)} + /> + +
    ); @@ -289,12 +230,13 @@ export function ReactionToggleButton({ title={t("action.pick_reaction")} hideHeader classNameModal={styles.reactionPopupMenuModal} + className={styles.reactionPopupMenuRoot} onDismiss={() => setShowReactionsMenu(false)} > void sendRelation(reaction)} toggleRaisedHand={wrappedToggleRaisedHand} /> diff --git a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap index bee0bdb1..9424e5a0 100644 --- a/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap +++ b/src/button/__snapshots__/ReactionToggleButton.test.tsx.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Can close search 1`] = ` +exports[`Can close reaction dialog 1`] = `