From b9384fbb309f68e49e87afebc96a5b303116c4ee Mon Sep 17 00:00:00 2001 From: ckie Date: Sat, 25 Jun 2022 22:47:18 +0300 Subject: [PATCH 1/7] Implement MSC2545 (partially) Signed-off-by: ckie --- res/css/_components.scss | 336 ++++++++++++++++++ res/css/views/rooms/_2545Stickers.scss | 35 ++ .../views/rooms/MSC2545StickerPicker.tsx | 150 ++++++++ .../views/rooms/MessageComposer.tsx | 4 +- 4 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 res/css/_components.scss create mode 100644 res/css/views/rooms/_2545Stickers.scss create mode 100644 src/components/views/rooms/MSC2545StickerPicker.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss new file mode 100644 index 00000000000..f37386ec9d2 --- /dev/null +++ b/res/css/_components.scss @@ -0,0 +1,336 @@ +// autogenerated by rethemendex.sh +@import "./_animations.scss"; +@import "./_common.scss"; +@import "./_font-sizes.scss"; +@import "./_font-weights.scss"; +@import "./_spacing.scss"; +@import "./components/views/beacon/_BeaconListItem.scss"; +@import "./components/views/beacon/_BeaconStatus.scss"; +@import "./components/views/beacon/_BeaconStatusTooltip.scss"; +@import "./components/views/beacon/_BeaconViewDialog.scss"; +@import "./components/views/beacon/_DialogOwnBeaconStatus.scss"; +@import "./components/views/beacon/_DialogSidebar.scss"; +@import "./components/views/beacon/_LeftPanelLiveShareWarning.scss"; +@import "./components/views/beacon/_LiveTimeRemaining.scss"; +@import "./components/views/beacon/_OwnBeaconStatus.scss"; +@import "./components/views/beacon/_RoomLiveShareWarning.scss"; +@import "./components/views/beacon/_ShareLatestLocation.scss"; +@import "./components/views/beacon/_StyledLiveBeaconIcon.scss"; +@import "./components/views/location/_EnableLiveShare.scss"; +@import "./components/views/location/_LiveDurationDropdown.scss"; +@import "./components/views/location/_LocationShareMenu.scss"; +@import "./components/views/location/_MapError.scss"; +@import "./components/views/location/_MapFallback.scss"; +@import "./components/views/location/_Marker.scss"; +@import "./components/views/location/_ShareDialogButtons.scss"; +@import "./components/views/location/_ShareType.scss"; +@import "./components/views/location/_ZoomButtons.scss"; +@import "./components/views/messages/_MBeaconBody.scss"; +@import "./components/views/spaces/_QuickThemeSwitcher.scss"; +@import "./structures/_AutoHideScrollbar.scss"; +@import "./structures/_BackdropPanel.scss"; +@import "./structures/_CompatibilityPage.scss"; +@import "./structures/_ContextualMenu.scss"; +@import "./structures/_FileDropTarget.scss"; +@import "./structures/_FilePanel.scss"; +@import "./structures/_GenericDropdownMenu.scss"; +@import "./structures/_GenericErrorPage.scss"; +@import "./structures/_HeaderButtons.scss"; +@import "./structures/_HomePage.scss"; +@import "./structures/_LeftPanel.scss"; +@import "./structures/_MainSplit.scss"; +@import "./structures/_MatrixChat.scss"; +@import "./structures/_NonUrgentToastContainer.scss"; +@import "./structures/_NotificationPanel.scss"; +@import "./structures/_QuickSettingsButton.scss"; +@import "./structures/_RightPanel.scss"; +@import "./structures/_RoomDirectory.scss"; +@import "./structures/_RoomSearch.scss"; +@import "./structures/_RoomStatusBar.scss"; +@import "./structures/_RoomView.scss"; +@import "./structures/_ScrollPanel.scss"; +@import "./structures/_SearchBox.scss"; +@import "./structures/_SpaceHierarchy.scss"; +@import "./structures/_SpacePanel.scss"; +@import "./structures/_SpaceRoomView.scss"; +@import "./structures/_TabbedView.scss"; +@import "./structures/_ToastContainer.scss"; +@import "./structures/_UploadBar.scss"; +@import "./structures/_UserMenu.scss"; +@import "./structures/_VideoRoomView.scss"; +@import "./structures/_ViewSource.scss"; +@import "./structures/auth/_CompleteSecurity.scss"; +@import "./structures/auth/_Login.scss"; +@import "./structures/auth/_Registration.scss"; +@import "./structures/auth/_SetupEncryptionBody.scss"; +@import "./views/audio_messages/_AudioPlayer.scss"; +@import "./views/audio_messages/_PlayPauseButton.scss"; +@import "./views/audio_messages/_PlaybackContainer.scss"; +@import "./views/audio_messages/_SeekBar.scss"; +@import "./views/audio_messages/_Waveform.scss"; +@import "./views/auth/_AuthBody.scss"; +@import "./views/auth/_AuthButtons.scss"; +@import "./views/auth/_AuthFooter.scss"; +@import "./views/auth/_AuthHeader.scss"; +@import "./views/auth/_AuthHeaderLogo.scss"; +@import "./views/auth/_AuthPage.scss"; +@import "./views/auth/_CompleteSecurityBody.scss"; +@import "./views/auth/_CountryDropdown.scss"; +@import "./views/auth/_InteractiveAuthEntryComponents.scss"; +@import "./views/auth/_LanguageSelector.scss"; +@import "./views/auth/_PassphraseField.scss"; +@import "./views/auth/_Welcome.scss"; +@import "./views/avatars/_BaseAvatar.scss"; +@import "./views/avatars/_DecoratedRoomAvatar.scss"; +@import "./views/avatars/_WidgetAvatar.scss"; +@import "./views/beta/_BetaCard.scss"; +@import "./views/context_menus/_CallContextMenu.scss"; +@import "./views/context_menus/_DeviceContextMenu.scss"; +@import "./views/context_menus/_IconizedContextMenu.scss"; +@import "./views/context_menus/_MessageContextMenu.scss"; +@import "./views/dialogs/_AddExistingToSpaceDialog.scss"; +@import "./views/dialogs/_AnalyticsLearnMoreDialog.scss"; +@import "./views/dialogs/_BugReportDialog.scss"; +@import "./views/dialogs/_BulkRedactDialog.scss"; +@import "./views/dialogs/_ChangelogDialog.scss"; +@import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss"; +@import "./views/dialogs/_CompoundDialog.scss"; +@import "./views/dialogs/_ConfirmSpaceUserActionDialog.scss"; +@import "./views/dialogs/_ConfirmUserActionDialog.scss"; +@import "./views/dialogs/_CreateRoomDialog.scss"; +@import "./views/dialogs/_CreateSubspaceDialog.scss"; +@import "./views/dialogs/_DeactivateAccountDialog.scss"; +@import "./views/dialogs/_DevtoolsDialog.scss"; +@import "./views/dialogs/_ExportDialog.scss"; +@import "./views/dialogs/_FeedbackDialog.scss"; +@import "./views/dialogs/_ForwardDialog.scss"; +@import "./views/dialogs/_GenericFeatureFeedbackDialog.scss"; +@import "./views/dialogs/_HostSignupDialog.scss"; +@import "./views/dialogs/_IncomingSasDialog.scss"; +@import "./views/dialogs/_InviteDialog.scss"; +@import "./views/dialogs/_JoinRuleDropdown.scss"; +@import "./views/dialogs/_LeaveSpaceDialog.scss"; +@import "./views/dialogs/_LocationViewDialog.scss"; +@import "./views/dialogs/_ManageRestrictedJoinRuleDialog.scss"; +@import "./views/dialogs/_MessageEditHistoryDialog.scss"; +@import "./views/dialogs/_ModalWidgetDialog.scss"; +@import "./views/dialogs/_NewSessionReviewDialog.scss"; +@import "./views/dialogs/_PollCreateDialog.scss"; +@import "./views/dialogs/_RegistrationEmailPromptDialog.scss"; +@import "./views/dialogs/_RoomSettingsDialog.scss"; +@import "./views/dialogs/_RoomSettingsDialogBridges.scss"; +@import "./views/dialogs/_RoomUpgradeDialog.scss"; +@import "./views/dialogs/_RoomUpgradeWarningDialog.scss"; +@import "./views/dialogs/_ServerOfflineDialog.scss"; +@import "./views/dialogs/_ServerPickerDialog.scss"; +@import "./views/dialogs/_SetEmailDialog.scss"; +@import "./views/dialogs/_SettingsDialog.scss"; +@import "./views/dialogs/_ShareDialog.scss"; +@import "./views/dialogs/_SlashCommandHelpDialog.scss"; +@import "./views/dialogs/_SpacePreferencesDialog.scss"; +@import "./views/dialogs/_SpaceSettingsDialog.scss"; +@import "./views/dialogs/_SpotlightDialog.scss"; +@import "./views/dialogs/_TermsDialog.scss"; +@import "./views/dialogs/_UntrustedDeviceDialog.scss"; +@import "./views/dialogs/_UploadConfirmDialog.scss"; +@import "./views/dialogs/_UserSettingsDialog.scss"; +@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.scss"; +@import "./views/dialogs/security/_AccessSecretStorageDialog.scss"; +@import "./views/dialogs/security/_CreateCrossSigningDialog.scss"; +@import "./views/dialogs/security/_CreateKeyBackupDialog.scss"; +@import "./views/dialogs/security/_CreateSecretStorageDialog.scss"; +@import "./views/dialogs/security/_KeyBackupFailedDialog.scss"; +@import "./views/dialogs/security/_RestoreKeyBackupDialog.scss"; +@import "./views/directory/_NetworkDropdown.scss"; +@import "./views/elements/_AccessibleButton.scss"; +@import "./views/elements/_AddressSelector.scss"; +@import "./views/elements/_AddressTile.scss"; +@import "./views/elements/_CopyableText.scss"; +@import "./views/elements/_DesktopCapturerSourcePicker.scss"; +@import "./views/elements/_DialPadBackspaceButton.scss"; +@import "./views/elements/_DirectorySearchBox.scss"; +@import "./views/elements/_Dropdown.scss"; +@import "./views/elements/_EditableItemList.scss"; +@import "./views/elements/_ErrorBoundary.scss"; +@import "./views/elements/_EventTilePreview.scss"; +@import "./views/elements/_ExternalLink.scss"; +@import "./views/elements/_FacePile.scss"; +@import "./views/elements/_Field.scss"; +@import "./views/elements/_GenericEventListSummary.scss"; +@import "./views/elements/_ImageView.scss"; +@import "./views/elements/_InfoTooltip.scss"; +@import "./views/elements/_InlineSpinner.scss"; +@import "./views/elements/_InteractiveTooltip.scss"; +@import "./views/elements/_InviteReason.scss"; +@import "./views/elements/_LabelledCheckbox.scss"; +@import "./views/elements/_ManageIntegsButton.scss"; +@import "./views/elements/_MiniAvatarUploader.scss"; +@import "./views/elements/_Pill.scss"; +@import "./views/elements/_PowerSelector.scss"; +@import "./views/elements/_ProgressBar.scss"; +@import "./views/elements/_QRCode.scss"; +@import "./views/elements/_ReplyChain.scss"; +@import "./views/elements/_ResizeHandle.scss"; +@import "./views/elements/_RichText.scss"; +@import "./views/elements/_RoleButton.scss"; +@import "./views/elements/_RoomAliasField.scss"; +@import "./views/elements/_SSOButtons.scss"; +@import "./views/elements/_SearchWarning.scss"; +@import "./views/elements/_ServerPicker.scss"; +@import "./views/elements/_SettingsFlag.scss"; +@import "./views/elements/_Slider.scss"; +@import "./views/elements/_Spinner.scss"; +@import "./views/elements/_StyledCheckbox.scss"; +@import "./views/elements/_StyledRadioButton.scss"; +@import "./views/elements/_SyntaxHighlight.scss"; +@import "./views/elements/_TagComposer.scss"; +@import "./views/elements/_TextWithTooltip.scss"; +@import "./views/elements/_ToggleSwitch.scss"; +@import "./views/elements/_Tooltip.scss"; +@import "./views/elements/_TooltipButton.scss"; +@import "./views/elements/_Validation.scss"; +@import "./views/emojipicker/_EmojiPicker.scss"; +@import "./views/location/_LocationPicker.scss"; +@import "./views/messages/_CallEvent.scss"; +@import "./views/messages/_CreateEvent.scss"; +@import "./views/messages/_DateSeparator.scss"; +@import "./views/messages/_DisambiguatedProfile.scss"; +@import "./views/messages/_EventTileBubble.scss"; +@import "./views/messages/_HiddenBody.scss"; +@import "./views/messages/_JumpToDatePicker.scss"; +@import "./views/messages/_MEmoteBody.scss"; +@import "./views/messages/_MFileBody.scss"; +@import "./views/messages/_MImageBody.scss"; +@import "./views/messages/_MImageReplyBody.scss"; +@import "./views/messages/_MJitsiWidgetEvent.scss"; +@import "./views/messages/_MLocationBody.scss"; +@import "./views/messages/_MNoticeBody.scss"; +@import "./views/messages/_MPollBody.scss"; +@import "./views/messages/_MStickerBody.scss"; +@import "./views/messages/_MTextBody.scss"; +@import "./views/messages/_MVideoBody.scss"; +@import "./views/messages/_MediaBody.scss"; +@import "./views/messages/_MessageActionBar.scss"; +@import "./views/messages/_MessageTimestamp.scss"; +@import "./views/messages/_MjolnirBody.scss"; +@import "./views/messages/_ReactionsRow.scss"; +@import "./views/messages/_ReactionsRowButton.scss"; +@import "./views/messages/_RedactedBody.scss"; +@import "./views/messages/_RoomAvatarEvent.scss"; +@import "./views/messages/_TextualEvent.scss"; +@import "./views/messages/_UnknownBody.scss"; +@import "./views/messages/_ViewSourceEvent.scss"; +@import "./views/messages/_common_CryptoEvent.scss"; +@import "./views/right_panel/_BaseCard.scss"; +@import "./views/right_panel/_EncryptionInfo.scss"; +@import "./views/right_panel/_PinnedMessagesCard.scss"; +@import "./views/right_panel/_RoomSummaryCard.scss"; +@import "./views/right_panel/_ThreadPanel.scss"; +@import "./views/right_panel/_TimelineCard.scss"; +@import "./views/right_panel/_UserInfo.scss"; +@import "./views/right_panel/_VerificationPanel.scss"; +@import "./views/right_panel/_WidgetCard.scss"; +@import "./views/room_settings/_AliasSettings.scss"; +@import "./views/rooms/_2545Stickers.scss"; +@import "./views/rooms/_AppsDrawer.scss"; +@import "./views/rooms/_Autocomplete.scss"; +@import "./views/rooms/_AuxPanel.scss"; +@import "./views/rooms/_BasicMessageComposer.scss"; +@import "./views/rooms/_E2EIcon.scss"; +@import "./views/rooms/_EditMessageComposer.scss"; +@import "./views/rooms/_EntityTile.scss"; +@import "./views/rooms/_EventBubbleTile.scss"; +@import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_HistoryTile.scss"; +@import "./views/rooms/_IRCLayout.scss"; +@import "./views/rooms/_JumpToBottomButton.scss"; +@import "./views/rooms/_LinkPreviewGroup.scss"; +@import "./views/rooms/_LinkPreviewWidget.scss"; +@import "./views/rooms/_MemberInfo.scss"; +@import "./views/rooms/_MemberList.scss"; +@import "./views/rooms/_MessageComposer.scss"; +@import "./views/rooms/_MessageComposerFormatBar.scss"; +@import "./views/rooms/_NewRoomIntro.scss"; +@import "./views/rooms/_NotificationBadge.scss"; +@import "./views/rooms/_PinnedEventTile.scss"; +@import "./views/rooms/_PresenceLabel.scss"; +@import "./views/rooms/_ReadReceiptGroup.scss"; +@import "./views/rooms/_RecentlyViewedButton.scss"; +@import "./views/rooms/_ReplyPreview.scss"; +@import "./views/rooms/_ReplyTile.scss"; +@import "./views/rooms/_RoomBreadcrumbs.scss"; +@import "./views/rooms/_RoomHeader.scss"; +@import "./views/rooms/_RoomInfoLine.scss"; +@import "./views/rooms/_RoomList.scss"; +@import "./views/rooms/_RoomListHeader.scss"; +@import "./views/rooms/_RoomPreviewBar.scss"; +@import "./views/rooms/_RoomPreviewCard.scss"; +@import "./views/rooms/_RoomSublist.scss"; +@import "./views/rooms/_RoomTile.scss"; +@import "./views/rooms/_RoomUpgradeWarningBar.scss"; +@import "./views/rooms/_SearchBar.scss"; +@import "./views/rooms/_SendMessageComposer.scss"; +@import "./views/rooms/_Stickers.scss"; +@import "./views/rooms/_ThreadSummary.scss"; +@import "./views/rooms/_TopUnreadMessagesBar.scss"; +@import "./views/rooms/_VideoRoomSummary.scss"; +@import "./views/rooms/_VoiceRecordComposerTile.scss"; +@import "./views/rooms/_WhoIsTypingTile.scss"; +@import "./views/settings/_AvatarSetting.scss"; +@import "./views/settings/_CrossSigningPanel.scss"; +@import "./views/settings/_CryptographyPanel.scss"; +@import "./views/settings/_DevicesPanel.scss"; +@import "./views/settings/_EmailAddresses.scss"; +@import "./views/settings/_FontScalingPanel.scss"; +@import "./views/settings/_ImageSizePanel.scss"; +@import "./views/settings/_IntegrationManager.scss"; +@import "./views/settings/_JoinRuleSettings.scss"; +@import "./views/settings/_KeyboardShortcut.scss"; +@import "./views/settings/_LayoutSwitcher.scss"; +@import "./views/settings/_Notifications.scss"; +@import "./views/settings/_PhoneNumbers.scss"; +@import "./views/settings/_ProfileSettings.scss"; +@import "./views/settings/_SecureBackupPanel.scss"; +@import "./views/settings/_SetIdServer.scss"; +@import "./views/settings/_SetIntegrationManager.scss"; +@import "./views/settings/_SettingsFieldset.scss"; +@import "./views/settings/_SpellCheckLanguages.scss"; +@import "./views/settings/_ThemeChoicePanel.scss"; +@import "./views/settings/_UpdateCheckButton.scss"; +@import "./views/settings/tabs/_SettingsTab.scss"; +@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; +@import "./views/settings/tabs/room/_NotificationSettingsTab.scss"; +@import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; +@import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; +@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_KeyboardUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_LabsUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_SidebarUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss"; +@import "./views/spaces/_SpaceBasicSettings.scss"; +@import "./views/spaces/_SpaceChildrenPicker.scss"; +@import "./views/spaces/_SpaceCreateMenu.scss"; +@import "./views/spaces/_SpacePublicShare.scss"; +@import "./views/terms/_InlineTermsAgreement.scss"; +@import "./views/toasts/_AnalyticsToast.scss"; +@import "./views/toasts/_IncomingCallToast.scss"; +@import "./views/toasts/_NonUrgentEchoFailureToast.scss"; +@import "./views/typography/_Heading.scss"; +@import "./views/verification/_VerificationShowSas.scss"; +@import "./views/voip/CallView/_CallViewButtons.scss"; +@import "./views/voip/_CallPreview.scss"; +@import "./views/voip/_CallView.scss"; +@import "./views/voip/_CallViewForRoom.scss"; +@import "./views/voip/_CallViewHeader.scss"; +@import "./views/voip/_CallViewSidebar.scss"; +@import "./views/voip/_DialPad.scss"; +@import "./views/voip/_DialPadContextMenu.scss"; +@import "./views/voip/_DialPadModal.scss"; +@import "./views/voip/_PiPContainer.scss"; +@import "./views/voip/_VideoFeed.scss"; +@import "./views/voip/_VideoLobby.scss"; diff --git a/res/css/views/rooms/_2545Stickers.scss b/res/css/views/rooms/_2545Stickers.scss new file mode 100644 index 00000000000..bbc32b39b50 --- /dev/null +++ b/res/css/views/rooms/_2545Stickers.scss @@ -0,0 +1,35 @@ +// .mx_2545Stickers_* + +.mx_2545Stickers_root { + overflow: scroll; + height: 500px; + .mx_RoomView_MessageList { + // grrr + height: unset !important; + } + .mx_EmojiPicker_search { + margin: 0px 0px 8px 0px; + } +} + +.mx_2545Stickers_grid { + display: flex; + flex-wrap: wrap; + max-width: 300px; + margin: 0 auto; +} + +.mx_2545Stickers_imageContainer { + flex-shrink: 1; + flex-basis: calc(25% - 10px); + margin: 5px; + user-select: none; + img { + width: 100%; + object-fit: contain; + } +} + +.mx_2545Stickers_label { + margin-top: 0px; +} diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx new file mode 100644 index 00000000000..2c03b532374 --- /dev/null +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -0,0 +1,150 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017, 2018 Vector Creations Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019, 2020, 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { IImageInfo, Room } from 'matrix-js-sdk/src/matrix'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; + +import MatrixClientContext from '../../../contexts/MatrixClientContext'; +import { mediaFromMxc } from '../../../customisations/Media'; +import ContextMenu, { ChevronFace } from '../../structures/ContextMenu'; +import ScrollPanel from '../../structures/ScrollPanel'; +import GenericElementContextMenu from '../context_menus/GenericElementContextMenu'; +import Search from '../emojipicker/Search'; + +const EMOTE_ROOMS_EVENT_TYPE = "im.ponies.emote_rooms"; +const ROOM_EMOTES_EVENT_TYPE = "im.ponies.room_emotes"; + +// Css Class; it's a short name for easy usage. +const cc = (thing: string) => "mx_2545Stickers_" + thing; + +interface I2545Image { + body: string; + info: IImageInfo; + url: string; // mxc + usage: ("sticker" | "emoticon")[]; +} + +interface I2545Pack { + pack: { + attribution: string; + avatar_url: string; + display_name: string; + }; + images: { + [id: string]: I2545Image; + }; +} + +export const MSC2545StickerPicker: React.FC<{ + room: Room; + threadId: string; + menuPosition?: any; + isStickerPickerOpen: boolean; + setStickerPickerOpen: (isStickerPickerOpen: boolean) => void; +}> = ({ room, threadId, menuPosition, isStickerPickerOpen, setStickerPickerOpen }) => { + const cli = useContext(MatrixClientContext); + const [searchFilter, setSearchFilter] = useState(""); + if (!isStickerPickerOpen) return null; + + const evt = cli.getAccountData(EMOTE_ROOMS_EVENT_TYPE); + const evtContent = evt.event.content as { rooms: { [roomId: string]: { [packName: string]: {} } } }; + + const packImage = (image: I2545Image) => { + const media = mediaFromMxc(image.url, cli); + // noinspection JSIgnoredPromiseFromCall + media.downloadSource(); + const onImageClick = (imgEvt: React.MouseEvent) => { + imgEvt.preventDefault(); + cli.sendStickerMessage(room.roomId, threadId, media.srcMxc, image.info, image.body); + setStickerPickerOpen(false); + }; + + // eslint-disable-next-line new-cap + return
+ +
; + }; + + const packs = Object.keys(evtContent.rooms) + .map(roomId => { + const room = cli.getRoom(roomId); + return Object.keys(evtContent.rooms[roomId]) + .map(name => { + const pack = room.currentState.getStateEvents(ROOM_EMOTES_EVENT_TYPE, name) + .event.content as I2545Pack; + return { room, pack, packName: name }; + }); + }).flat(1); + + const finished = () => { + if (isStickerPickerOpen) { + setStickerPickerOpen(false); + } + }; + + const renderedPacks = packs.map(({ pack, packName }) => { + const lcFilter = searchFilter.toLowerCase().trim(); // filter is case insensitive + const images = Object.values(pack.images) + .filter(im => im.body.toLowerCase().includes(lcFilter)); + + if (images.length == 0) return; + + return
+

{pack.pack.display_name}

+
+ {images.map(packImage)} +
+
; + }); + + + return + + { }} /> + {renderedPacks} + } + onResize={finished} /> + ; +}; + +/* const accountDataHandler = useCallback((ev) => { +* console.log("ckalm", ev); +* // TODO +* }, [cli]); +* useTypedEventEmitter(cli, ClientEvent.AccountData, accountDataHandler); +*/ +// Count of how many operations are currently in progress, if > 0 then show a Spinner +/* const [pendingUpdateCount, setPendingUpdateCount] = useState(0); */ +/* const startUpdating = useCallback(() => { +* setPendingUpdateCount(pendingUpdateCount + 1); +* }, [pendingUpdateCount]); +* const stopUpdating = useCallback(() => { +* setPendingUpdateCount(pendingUpdateCount - 1); +* }, [pendingUpdateCount]); */ diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index cf0fe3fd6ad..c9429046fdb 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -27,7 +27,6 @@ import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import dis from '../../../dispatcher/dispatcher'; import { ActionPayload } from "../../../dispatcher/payloads"; -import Stickerpicker from './Stickerpicker'; import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import E2EIcon from './E2EIcon'; import SettingsStore from "../../../settings/SettingsStore"; @@ -58,6 +57,7 @@ import { startNewVoiceBroadcastRecording, VoiceBroadcastRecordingsStore, } from '../../../voice-broadcast'; +import { MSC2545StickerPicker } from './MSC2545StickerPicker'; let instanceCount = 0; @@ -461,7 +461,7 @@ export default class MessageComposer extends React.Component { : null; controls.push( - Date: Sun, 26 Jun 2022 21:15:31 +0300 Subject: [PATCH 2/7] Nicer CSS for hovering over a sticker Signed-off-by: ckie --- res/css/views/rooms/_2545Stickers.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/res/css/views/rooms/_2545Stickers.scss b/res/css/views/rooms/_2545Stickers.scss index bbc32b39b50..f9b9fa7f5d6 100644 --- a/res/css/views/rooms/_2545Stickers.scss +++ b/res/css/views/rooms/_2545Stickers.scss @@ -24,10 +24,21 @@ flex-basis: calc(25% - 10px); margin: 5px; user-select: none; + border-radius: 4px 4px 0 0; + img { width: 100%; object-fit: contain; } + + &:not(:disabled) { + cursor: pointer; + } + + &:not(:disabled):hover { + background-color: $focus-bg-color; + border-bottom: 2px solid $accent; + } } .mx_2545Stickers_label { From 27fb1d27d4dbd91b29b9c65356f129651e688f4f Mon Sep 17 00:00:00 2001 From: ckie Date: Sun, 26 Jun 2022 21:15:55 +0300 Subject: [PATCH 3/7] Extract PackImage into its own thing Signed-off-by: ckie --- .../views/rooms/MSC2545StickerPicker.tsx | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx index 2c03b532374..f954fdcb6ed 100644 --- a/src/components/views/rooms/MSC2545StickerPicker.tsx +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -51,6 +51,32 @@ interface I2545Pack { }; } +const PackImage: React.FC<{ + image: I2545Image, + roomId: string, + threadId: string, + setStickerPickerOpen: (isStickerPickerOpen: boolean) => void; +}> = ({ image, roomId, threadId, setStickerPickerOpen }) => { + const cli = useContext(MatrixClientContext); + const media = mediaFromMxc(image.url, cli); + // noinspection JSIgnoredPromiseFromCall + + const onImageClick = (imgEvt: React.MouseEvent) => { + imgEvt.preventDefault(); + cli.sendStickerMessage(roomId, threadId, media.srcMxc, image.info, image.body); + setStickerPickerOpen(false); + }; + + if (media.isEncrypted) return +

Stickers from encrypted rooms aren't supported {"):"}

; + + // eslint-disable-next-line new-cap + return
+ {image.body} +
; +}; + + export const MSC2545StickerPicker: React.FC<{ room: Room; threadId: string; @@ -65,22 +91,6 @@ export const MSC2545StickerPicker: React.FC<{ const evt = cli.getAccountData(EMOTE_ROOMS_EVENT_TYPE); const evtContent = evt.event.content as { rooms: { [roomId: string]: { [packName: string]: {} } } }; - const packImage = (image: I2545Image) => { - const media = mediaFromMxc(image.url, cli); - // noinspection JSIgnoredPromiseFromCall - media.downloadSource(); - const onImageClick = (imgEvt: React.MouseEvent) => { - imgEvt.preventDefault(); - cli.sendStickerMessage(room.roomId, threadId, media.srcMxc, image.info, image.body); - setStickerPickerOpen(false); - }; - - // eslint-disable-next-line new-cap - return
- -
; - }; - const packs = Object.keys(evtContent.rooms) .map(roomId => { const room = cli.getRoom(roomId); @@ -108,7 +118,12 @@ export const MSC2545StickerPicker: React.FC<{ return

{pack.pack.display_name}

- {images.map(packImage)} + {images.map((im, idx) => )}
; }); From 9d7feca2ab02583f5038d0c07b86aa3ff2fe49d1 Mon Sep 17 00:00:00 2001 From: ckie Date: Sun, 26 Jun 2022 21:23:09 +0300 Subject: [PATCH 4/7] Use 2545's terminology instead of ponies.im's Signed-off-by: ckie --- src/components/views/rooms/MSC2545StickerPicker.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx index f954fdcb6ed..7f8482a1368 100644 --- a/src/components/views/rooms/MSC2545StickerPicker.tsx +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -27,8 +27,10 @@ import ScrollPanel from '../../structures/ScrollPanel'; import GenericElementContextMenu from '../context_menus/GenericElementContextMenu'; import Search from '../emojipicker/Search'; -const EMOTE_ROOMS_EVENT_TYPE = "im.ponies.emote_rooms"; -const ROOM_EMOTES_EVENT_TYPE = "im.ponies.room_emotes"; +// TODO: support more image sources: +// https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md#image-sources +const USER_PACK_ROOMS_EVENT_TYPE = "im.ponies.emote_rooms"; +const PACK_ROOM_EVENT_TYPE = "im.ponies.room_emotes"; // Css Class; it's a short name for easy usage. const cc = (thing: string) => "mx_2545Stickers_" + thing; @@ -88,7 +90,7 @@ export const MSC2545StickerPicker: React.FC<{ const [searchFilter, setSearchFilter] = useState(""); if (!isStickerPickerOpen) return null; - const evt = cli.getAccountData(EMOTE_ROOMS_EVENT_TYPE); + const evt = cli.getAccountData(USER_PACK_ROOMS_EVENT_TYPE); const evtContent = evt.event.content as { rooms: { [roomId: string]: { [packName: string]: {} } } }; const packs = Object.keys(evtContent.rooms) @@ -96,7 +98,7 @@ export const MSC2545StickerPicker: React.FC<{ const room = cli.getRoom(roomId); return Object.keys(evtContent.rooms[roomId]) .map(name => { - const pack = room.currentState.getStateEvents(ROOM_EMOTES_EVENT_TYPE, name) + const pack = room.currentState.getStateEvents(PACK_ROOM_EVENT_TYPE, name) .event.content as I2545Pack; return { room, pack, packName: name }; }); From 3da32524fa2723d895e1ecb516492a9934aeef8c Mon Sep 17 00:00:00 2001 From: ckie Date: Sun, 26 Jun 2022 21:56:47 +0300 Subject: [PATCH 5/7] Implement 's `onEnter` callback Signed-off-by: ckie --- .../views/rooms/MSC2545StickerPicker.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx index 7f8482a1368..537591ea7c6 100644 --- a/src/components/views/rooms/MSC2545StickerPicker.tsx +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -18,7 +18,7 @@ limitations under the License. */ import { IImageInfo, Room } from 'matrix-js-sdk/src/matrix'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import React, { useContext, useState } from 'react'; import MatrixClientContext from '../../../contexts/MatrixClientContext'; import { mediaFromMxc } from '../../../customisations/Media'; @@ -57,8 +57,9 @@ const PackImage: React.FC<{ image: I2545Image, roomId: string, threadId: string, - setStickerPickerOpen: (isStickerPickerOpen: boolean) => void; -}> = ({ image, roomId, threadId, setStickerPickerOpen }) => { + setStickerPickerOpen: (isStickerPickerOpen: boolean) => void, + innerRef: React.RefObject | null +}> = ({ image, roomId, threadId, setStickerPickerOpen, innerRef }) => { const cli = useContext(MatrixClientContext); const media = mediaFromMxc(image.url, cli); // noinspection JSIgnoredPromiseFromCall @@ -74,7 +75,7 @@ const PackImage: React.FC<{ // eslint-disable-next-line new-cap return
- {image.body} + {image.body}
; }; @@ -104,13 +105,18 @@ export const MSC2545StickerPicker: React.FC<{ }); }).flat(1); - const finished = () => { + const topStickerRef = React.createRef(); + + const finished = (send: boolean) => () => { if (isStickerPickerOpen) { + if (send) topStickerRef.current.click(); setStickerPickerOpen(false); + setSearchFilter(""); } }; - const renderedPacks = packs.map(({ pack, packName }) => { + + const renderedPacks = packs.map(({ pack, packName }, packIdx) => { const lcFilter = searchFilter.toLowerCase().trim(); // filter is case insensitive const images = Object.values(pack.images) .filter(im => im.body.toLowerCase().includes(lcFilter)); @@ -121,6 +127,7 @@ export const MSC2545StickerPicker: React.FC<{

{pack.pack.display_name}

{images.map((im, idx) => - { }} /> + {renderedPacks} } - onResize={finished} /> + onResize={finished(false)} /> ; }; From dbea205d97cd318bb3433f765c31541eca6c7169 Mon Sep 17 00:00:00 2001 From: ckie Date: Thu, 30 Jun 2022 06:26:46 +0300 Subject: [PATCH 6/7] MSC2545StickerPicker: add some TODOs --- src/components/views/rooms/MSC2545StickerPicker.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx index 537591ea7c6..44f5ec62d19 100644 --- a/src/components/views/rooms/MSC2545StickerPicker.tsx +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -92,6 +92,7 @@ export const MSC2545StickerPicker: React.FC<{ if (!isStickerPickerOpen) return null; const evt = cli.getAccountData(USER_PACK_ROOMS_EVENT_TYPE); + // TODO: check if null const evtContent = evt.event.content as { rooms: { [roomId: string]: { [packName: string]: {} } } }; const packs = Object.keys(evtContent.rooms) @@ -99,6 +100,7 @@ export const MSC2545StickerPicker: React.FC<{ const room = cli.getRoom(roomId); return Object.keys(evtContent.rooms[roomId]) .map(name => { + // TODO: check if null const pack = room.currentState.getStateEvents(PACK_ROOM_EVENT_TYPE, name) .event.content as I2545Pack; return { room, pack, packName: name }; From f84b7d56c988f9c83ec3e8a33498895d1d11a567 Mon Sep 17 00:00:00 2001 From: ckie Date: Tue, 26 Jul 2022 21:06:07 +0300 Subject: [PATCH 7/7] MSC2545StickerPicker: comply to optional datatypes --- .../views/rooms/MSC2545StickerPicker.tsx | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/MSC2545StickerPicker.tsx b/src/components/views/rooms/MSC2545StickerPicker.tsx index 44f5ec62d19..9e3c18c097e 100644 --- a/src/components/views/rooms/MSC2545StickerPicker.tsx +++ b/src/components/views/rooms/MSC2545StickerPicker.tsx @@ -36,17 +36,20 @@ const PACK_ROOM_EVENT_TYPE = "im.ponies.room_emotes"; const cc = (thing: string) => "mx_2545Stickers_" + thing; interface I2545Image { - body: string; - info: IImageInfo; url: string; // mxc - usage: ("sticker" | "emoticon")[]; + body?: string; + info?: IImageInfo; + // 2545: If present and non-empty, this overrides the usage defined at pack level for this particular image + usage?: ("sticker" | "emoticon")[]; } interface I2545Pack { pack: { - attribution: string; - avatar_url: string; - display_name: string; + attribution?: string; + // 2545: If the usage is absent or empty, a usage for all possible usage types is to be assumed. + usage?: ("emoticon" | "sticker")[]; + avatar_url?: string; + display_name?: string; }; images: { [id: string]: I2545Image; @@ -121,12 +124,13 @@ export const MSC2545StickerPicker: React.FC<{ const renderedPacks = packs.map(({ pack, packName }, packIdx) => { const lcFilter = searchFilter.toLowerCase().trim(); // filter is case insensitive const images = Object.values(pack.images) - .filter(im => im.body.toLowerCase().includes(lcFilter)); + .filter(im => (im.body || "").toLowerCase().includes(lcFilter)); if (images.length == 0) return; + const progressiveDisplayName = pack.pack.display_name || packName; return
-

{pack.pack.display_name}

+

{progressiveDisplayName}

{images.map((im, idx) =>