diff --git a/res/css/views/right_panel/_RoomSummaryCard.scss b/res/css/views/right_panel/_RoomSummaryCard.scss index 0031d3a64c..8878302435 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.scss +++ b/res/css/views/right_panel/_RoomSummaryCard.scss @@ -109,9 +109,57 @@ limitations under the License. } .mx_RoomSummaryCard_appsGroup { + .mx_RoomSummaryCard_widgetRow { + margin: 0; + display: flex; + + .mx_RoomSummaryCard_app_pinToggle, + .mx_RoomSummaryCard_app_options { + position: relative; + height: 20px; + width: 20px; + padding: 10px; + border-radius: 8px; + + &:hover { + background-color: rgba(141, 151, 165, 0.1); + } + + &::before { + content: ''; + position: absolute; + height: 20px; + width: 20px; + mask-repeat: no-repeat; + mask-position: center; + mask-size: 16px; + background-color: $icon-button-color; + } + } + + .mx_RoomSummaryCard_app_pinToggle { + &::before { + mask-image: url('$(res)/img/element-icons/room/pin-upright.svg'); + } + + &.mx_RoomSummaryCard_app_pinned { + &::before { + background-color: $accent-color; + } + } + } + + .mx_RoomSummaryCard_app_options { + &::before { + mask-image: url('$(res)/img/element-icons/room/ellipsis.svg'); + } + } + } + .mx_RoomSummaryCard_Button { padding-left: 12px; color: $tertiary-fg-color; + flex: 1; span { color: $primary-fg-color; @@ -127,12 +175,6 @@ limitations under the License. content: unset; } } - - .mx_RoomSummaryCard_icon_app_pinned::after { - mask-image: url('$(res)/img/element-icons/room/pin-upright.svg'); - background-color: $accent-color; - transform: unset; - } } .mx_AccessibleButton_kind_link { diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index 95b159deed..b821ca2bbd 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -43,6 +43,9 @@ import WidgetStore, {IApp} from "../../../stores/WidgetStore"; import { E2EStatus } from "../../../utils/ShieldUtils"; import RoomContext from "../../../contexts/RoomContext"; import {UIFeature} from "../../../settings/UIFeature"; +import {ContextMenuButton} from "../../../accessibility/context_menu/ContextMenuButton"; +import {ChevronFace, useContextMenu} from "../../structures/ContextMenu"; +import RoomWidgetContextMenu from "../context_menus/RoomWidgetContextMenu"; interface IProps { room: Room; @@ -82,8 +85,93 @@ export const useWidgets = (room: Room) => { return apps; }; -const AppsSection: React.FC = ({ room }) => { +interface IAppRowProps { + app: IApp; +} + +const AppRow: React.FC = ({ app }) => { const cli = useContext(MatrixClientContext); + + const name = WidgetUtils.getWidgetName(app); + const dataTitle = WidgetUtils.getWidgetDataTitle(app); + const subtitle = dataTitle && " - " + dataTitle; + + let iconUrls = [require("../../../../res/img/element-icons/room/default_app.svg")]; + // heuristics for some better icons until Widgets support their own icons + if (app.type.includes("meeting") || app.type.includes("calendar")) { + iconUrls = [require("../../../../res/img/element-icons/room/default_cal.svg")]; + } else if (app.type.includes("pad") || app.type.includes("doc") || app.type.includes("calc")) { + iconUrls = [require("../../../../res/img/element-icons/room/default_doc.svg")]; + } else if (app.type.includes("clock")) { + iconUrls = [require("../../../../res/img/element-icons/room/default_clock.svg")]; + } + + if (app.avatar_url) { // MSC2765 + iconUrls.unshift(getHttpUriForMxc(cli.getHomeserverUrl(), app.avatar_url, 20, 20, "crop")); + } + + const onOpenWidgetClick = () => { + defaultDispatcher.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.Widget, + refireParams: { + widgetId: app.id, + }, + }); + }; + + const isPinned = WidgetStore.instance.isPinned(app.id); + const togglePin = isPinned + ? () => { WidgetStore.instance.unpinWidget(app.id); } + : () => { WidgetStore.instance.pinWidget(app.id); }; + + const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); + let contextMenu; + if (menuDisplayed) { + const rect = handle.current.getBoundingClientRect(); + contextMenu = ; + } + + return
+ + + {name} + { subtitle } + + + + + + + { contextMenu } +
; +}; + +const AppsSection: React.FC = ({ room }) => { const apps = useWidgets(room); const onManageIntegrations = () => { @@ -100,65 +188,7 @@ const AppsSection: React.FC = ({ room }) => { }; return - { apps.map(app => { - const name = WidgetUtils.getWidgetName(app); - const dataTitle = WidgetUtils.getWidgetDataTitle(app); - const subtitle = dataTitle && " - " + dataTitle; - - let iconUrls = [require("../../../../res/img/element-icons/room/default_app.svg")]; - // heuristics for some better icons until Widgets support their own icons - if (app.type.includes("meeting") || app.type.includes("calendar")) { - iconUrls = [require("../../../../res/img/element-icons/room/default_cal.svg")]; - } else if (app.type.includes("pad") || app.type.includes("doc") || app.type.includes("calc")) { - iconUrls = [require("../../../../res/img/element-icons/room/default_doc.svg")]; - } else if (app.type.includes("clock")) { - iconUrls = [require("../../../../res/img/element-icons/room/default_clock.svg")]; - } - - if (app.avatar_url) { // MSC2765 - iconUrls.unshift(getHttpUriForMxc(cli.getHomeserverUrl(), app.avatar_url, 20, 20, "crop")); - } - - const isPinned = WidgetStore.instance.isPinned(app.id); - const classes = classNames("mx_RoomSummaryCard_icon_app", { - mx_RoomSummaryCard_icon_app_pinned: isPinned, - }); - - if (isPinned) { - const onClick = () => { - WidgetStore.instance.unpinWidget(app.id); - }; - - return - - {name} - { subtitle } - - } - - const onOpenWidgetClick = () => { - defaultDispatcher.dispatch({ - action: Action.SetRightPanelPhase, - phase: RightPanelPhases.Widget, - refireParams: { - widgetId: app.id, - }, - }); - }; - - return ( - - ); - }) } + { apps.map(app => ) } { apps.length > 0 ? _t("Edit widgets, bridges & bots") : _t("Add widgets, bridges & bots") } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 65374ea3ec..f1c8317c3c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1274,8 +1274,10 @@ "Yours, or the other users’ session": "Yours, or the other users’ session", "Members": "Members", "Room Info": "Room Info", + "You can't view pinned widgets in the right panel": "You can't view pinned widgets in the right panel", + "Unpin": "Unpin", + "Options": "Options", "Widgets": "Widgets", - "Unpin app": "Unpin app", "Edit widgets, bridges & bots": "Edit widgets, bridges & bots", "Add widgets, bridges & bots": "Add widgets, bridges & bots", "Not encrypted": "Not encrypted", @@ -1298,7 +1300,6 @@ "Invite": "Invite", "Share Link to User": "Share Link to User", "Direct message": "Direct message", - "Options": "Options", "Demote yourself?": "Demote yourself?", "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.": "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.", "Demote": "Demote", @@ -1362,9 +1363,6 @@ "You cancelled verification.": "You cancelled verification.", "Verification cancelled": "Verification cancelled", "Compare emoji": "Compare emoji", - "Take a picture": "Take a picture", - "Remove for everyone": "Remove for everyone", - "Remove for me": "Remove for me", "Edit": "Edit", "Pin to room": "Pin to room", "You can only pin 2 widgets at a time": "You can only pin 2 widgets at a time", @@ -1924,12 +1922,14 @@ "Source URL": "Source URL", "Collapse Reply Thread": "Collapse Reply Thread", "Report Content": "Report Content", + "Take a picture": "Take a picture", + "Remove for everyone": "Remove for everyone", + "Remove for me": "Remove for me", "Clear status": "Clear status", "Update status": "Update status", "Set status": "Set status", "Set a new status...": "Set a new status...", "View Community": "View Community", - "Unpin": "Unpin", "Reload": "Reload", "Take picture": "Take picture", "This room is public": "This room is public",