Iterate pinned messages
							parent
							
								
									fd74a946e0
								
							
						
					
					
						commit
						27ad90760d
					
				|  | @ -15,227 +15,21 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| .mx_PinnedMessagesCard { | ||||
|     padding-top: 0; | ||||
| 
 | ||||
|     .mx_BaseCard_header { | ||||
|         text-align: center; | ||||
|         margin-top: 20px; | ||||
|         margin-top: 0; | ||||
|         border-bottom: 1px solid $menu-border-color; | ||||
| 
 | ||||
|         h2 { | ||||
|         > h2 { | ||||
|             font-weight: $font-semi-bold; | ||||
|             font-size: $font-18px; | ||||
|             margin: 12px 0 4px; | ||||
|             margin: 8px 0; | ||||
|         } | ||||
| 
 | ||||
|         .mx_RoomSummaryCard_alias { | ||||
|             font-size: $font-13px; | ||||
|             color: $secondary-fg-color; | ||||
|         } | ||||
| 
 | ||||
|         h2, .mx_RoomSummaryCard_alias { | ||||
|             display: -webkit-box; | ||||
|             -webkit-line-clamp: 2; | ||||
|             -webkit-box-orient: vertical; | ||||
|             overflow: hidden; | ||||
|             text-overflow: ellipsis; | ||||
|             white-space: pre-wrap; | ||||
|         } | ||||
| 
 | ||||
|         .mx_RoomSummaryCard_avatar { | ||||
|             display: inline-flex; | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_e2ee { | ||||
|                 display: inline-block; | ||||
|                 position: relative; | ||||
|                 width: 54px; | ||||
|                 height: 54px; | ||||
|                 border-radius: 50%; | ||||
|                 background-color: #737d8c; | ||||
|                 margin-top: -3px; // alignment | ||||
|                 margin-left: -10px; // overlap | ||||
|                 border: 3px solid $dark-panel-bg-color; | ||||
| 
 | ||||
|                 &::before { | ||||
|                     content: ''; | ||||
|                     position: absolute; | ||||
|                     top: 13px; | ||||
|                     left: 13px; | ||||
|                     height: 28px; | ||||
|                     width: 28px; | ||||
|                     mask-size: cover; | ||||
|                     mask-repeat: no-repeat; | ||||
|                     mask-position: center; | ||||
|                     mask-image: url('$(res)/img/e2e/disabled.svg'); | ||||
|                     background-color: #ffffff; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_e2ee_normal { | ||||
|                 background-color: #424446; | ||||
|                 &::before { | ||||
|                     mask-image: url('$(res)/img/e2e/normal.svg'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_e2ee_verified { | ||||
|                 background-color: #0dbd8b; | ||||
|                 &::before { | ||||
|                     mask-image: url('$(res)/img/e2e/verified.svg'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_e2ee_warning { | ||||
|                 background-color: #ff4b55; | ||||
|                 &::before { | ||||
|                     mask-image: url('$(res)/img/e2e/warning.svg'); | ||||
|                 } | ||||
|             } | ||||
|         .mx_BaseCard_close { | ||||
|             margin-right: 6px; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_RoomSummaryCard_aboutGroup { | ||||
|         .mx_RoomSummaryCard_Button { | ||||
|             padding-left: 44px; | ||||
| 
 | ||||
|             &::before { | ||||
|                 content: ''; | ||||
|                 position: absolute; | ||||
|                 top: 8px; | ||||
|                 left: 10px; | ||||
|                 height: 24px; | ||||
|                 width: 24px; | ||||
|                 mask-repeat: no-repeat; | ||||
|                 mask-position: center; | ||||
|                 background-color: $icon-button-color; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_RoomSummaryCard_appsGroup { | ||||
|         .mx_RoomSummaryCard_Button { | ||||
|             // this button is special so we have to override some of the original styling | ||||
|             // as we will be applying it in its children | ||||
|             padding: 0; | ||||
|             height: auto; | ||||
|             color: $tertiary-fg-color; | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_icon_app { | ||||
|                 padding: 10px 48px 10px 12px; // based on typical mx_RoomSummaryCard_Button padding | ||||
|                 text-overflow: ellipsis; | ||||
|                 overflow: hidden; | ||||
| 
 | ||||
|                 .mx_BaseAvatar_image { | ||||
|                     vertical-align: top; | ||||
|                     margin-right: 12px; | ||||
|                 } | ||||
| 
 | ||||
|                 span { | ||||
|                     color: $primary-fg-color; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_app_pinToggle, | ||||
|             .mx_RoomSummaryCard_app_options { | ||||
|                 position: absolute; | ||||
|                 top: 0; | ||||
|                 height: 100%; // to give bigger interactive zone | ||||
|                 width: 24px; | ||||
|                 padding: 12px 4px; | ||||
|                 box-sizing: border-box; | ||||
|                 min-width: 24px; // prevent flexbox crushing | ||||
| 
 | ||||
|                 &:hover { | ||||
|                     &::after { | ||||
|                         content: ''; | ||||
|                         position: absolute; | ||||
|                         height: 24px; | ||||
|                         width: 24px; | ||||
|                         top: 8px; // equal to padding-top of parent | ||||
|                         left: 0; | ||||
|                         border-radius: 12px; | ||||
|                         background-color: rgba(141, 151, 165, 0.1); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 &::before { | ||||
|                     content: ''; | ||||
|                     position: absolute; | ||||
|                     height: 16px; | ||||
|                     width: 16px; | ||||
|                     mask-repeat: no-repeat; | ||||
|                     mask-position: center; | ||||
|                     mask-size: 16px; | ||||
|                     background-color: $icon-button-color; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_app_pinToggle { | ||||
|                 right: 24px; | ||||
| 
 | ||||
|                 &::before { | ||||
|                     mask-image: url('$(res)/img/element-icons/room/pin-upright.svg'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             .mx_RoomSummaryCard_app_options { | ||||
|                 right: 48px; | ||||
|                 display: none; | ||||
| 
 | ||||
|                 &::before { | ||||
|                     mask-image: url('$(res)/img/element-icons/room/ellipsis.svg'); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &.mx_RoomSummaryCard_Button_pinned { | ||||
|                 &::after { | ||||
|                     opacity: 0.2; | ||||
|                 } | ||||
| 
 | ||||
|                 .mx_RoomSummaryCard_app_pinToggle::before { | ||||
|                     background-color: $accent-color; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &:hover { | ||||
|                 .mx_RoomSummaryCard_icon_app { | ||||
|                     padding-right: 72px; | ||||
|                 } | ||||
| 
 | ||||
|                 .mx_RoomSummaryCard_app_options { | ||||
|                     display: unset; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             &::before { | ||||
|                 content: unset; | ||||
|             } | ||||
| 
 | ||||
|             &::after { | ||||
|                 top: 8px; // re-align based on the height change | ||||
|                 pointer-events: none; // pass through to the real button | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     .mx_AccessibleButton_kind_link { | ||||
|         padding: 0; | ||||
|         margin-top: 12px; | ||||
|         margin-bottom: 12px; | ||||
|         font-size: $font-13px; | ||||
|         font-weight: $font-semi-bold; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .mx_RoomSummaryCard_icon_people::before { | ||||
|     mask-image: url("$(res)/img/element-icons/room/members.svg"); | ||||
| } | ||||
| 
 | ||||
| .mx_RoomSummaryCard_icon_files::before { | ||||
|     mask-image: url('$(res)/img/element-icons/room/files.svg'); | ||||
| } | ||||
| 
 | ||||
| .mx_RoomSummaryCard_icon_share::before { | ||||
|     mask-image: url('$(res)/img/element-icons/room/share.svg'); | ||||
| } | ||||
| 
 | ||||
| .mx_RoomSummaryCard_icon_settings::before { | ||||
|     mask-image: url('$(res)/img/element-icons/settings.svg'); | ||||
| } | ||||
|  |  | |||
|  | @ -16,62 +16,90 @@ limitations under the License. | |||
| 
 | ||||
| .mx_PinnedEventTile { | ||||
|     min-height: 40px; | ||||
|     margin-bottom: 5px; | ||||
|     width: 100%; | ||||
|     border-radius: 5px; // for the hover | ||||
| } | ||||
|     padding: 0 4px 12px; | ||||
| 
 | ||||
| .mx_PinnedEventTile:hover { | ||||
|     background-color: $event-selected-color; | ||||
| } | ||||
|     display: grid; | ||||
|     grid-template-areas: "avatar name remove" | ||||
|                          "content content content" | ||||
|                          "footer footer footer"; | ||||
|     grid-template-rows: max-content auto max-content; | ||||
|     grid-template-columns: 24px auto 24px; | ||||
|     grid-row-gap: 12px; | ||||
|     grid-column-gap: 8px; | ||||
| 
 | ||||
| .mx_PinnedEventTile .mx_PinnedEventTile_sender, | ||||
| .mx_PinnedEventTile .mx_PinnedEventTile_timestamp { | ||||
|     color: #868686; | ||||
|     font-size: 0.8em; | ||||
|     vertical-align: top; | ||||
|     display: inline-block; | ||||
|     padding-bottom: 3px; | ||||
| } | ||||
|     & + .mx_PinnedEventTile { | ||||
|         padding: 12px 4px; | ||||
|         border-top: 1px solid $menu-border-color; | ||||
|     } | ||||
| 
 | ||||
| .mx_PinnedEventTile .mx_PinnedEventTile_timestamp { | ||||
|     padding-left: 15px; | ||||
|     display: none; | ||||
| } | ||||
|     .mx_PinnedEventTile_senderAvatar { | ||||
|         grid-area: avatar; | ||||
|     } | ||||
| 
 | ||||
| .mx_PinnedEventTile .mx_PinnedEventTile_senderAvatar .mx_BaseAvatar { | ||||
|     float: left; | ||||
|     margin-right: 10px; | ||||
| } | ||||
|     .mx_PinnedEventTile_sender { | ||||
|         grid-area: name; | ||||
|         font-weight: $font-semi-bold; | ||||
|         font-size: $font-15px; | ||||
|         line-height: $font-24px; | ||||
|         text-overflow: ellipsis; | ||||
|         overflow: hidden; | ||||
|         white-space: nowrap; | ||||
|     } | ||||
| 
 | ||||
| .mx_PinnedEventTile_actions { | ||||
|     float: right; | ||||
|     margin-right: 10px; | ||||
|     display: none; | ||||
| } | ||||
|     .mx_PinnedEventTile_unpinButton { | ||||
|         visibility: hidden; | ||||
|         grid-area: remove; | ||||
|         position: relative; | ||||
|         width: 24px; | ||||
|         height: 24px; | ||||
|         border-radius: 8px; | ||||
| 
 | ||||
| .mx_PinnedEventTile:hover .mx_PinnedEventTile_timestamp { | ||||
|     display: inline-block; | ||||
| } | ||||
|         &:hover { | ||||
|             background-color: $roomheader-addroom-bg-color; | ||||
|         } | ||||
| 
 | ||||
| .mx_PinnedEventTile:hover .mx_PinnedEventTile_actions { | ||||
|     display: block; | ||||
| } | ||||
|         &::before { | ||||
|             content: ""; | ||||
|             position: absolute; | ||||
|             //top: 0; | ||||
|             //left: 0; | ||||
|             height: inherit; | ||||
|             width: inherit; | ||||
|             background: $secondary-fg-color; | ||||
|             mask-position: center; | ||||
|             mask-size: 8px; | ||||
|             mask-repeat: no-repeat; | ||||
|             mask-image: url('$(res)/img/image-view/close.svg'); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| .mx_PinnedEventTile_unpinButton { | ||||
|     display: inline-block; | ||||
|     cursor: pointer; | ||||
|     margin-left: 10px; | ||||
| } | ||||
|     .mx_PinnedEventTile_message { | ||||
|         grid-area: content; | ||||
|     } | ||||
| 
 | ||||
| .mx_PinnedEventTile_gotoButton { | ||||
|     display: inline-block; | ||||
|     font-size: 0.7em; // Smaller text to avoid conflicting with the layout | ||||
| } | ||||
|     .mx_PinnedEventTile_footer { | ||||
|         grid-area: footer; | ||||
|         font-size: 10px; | ||||
|         line-height: 12px; | ||||
| 
 | ||||
| .mx_PinnedEventTile_message { | ||||
|     margin-left: 50px; | ||||
|     position: relative; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|         .mx_PinnedEventTile_timestamp { | ||||
|             font-size: inherit; | ||||
|             line-height: inherit; | ||||
|             color: $secondary-fg-color; | ||||
|         } | ||||
| 
 | ||||
|         .mx_AccessibleButton_kind_link { | ||||
|             padding: 0; | ||||
|             margin-left: 12px; | ||||
|             font-size: inherit; | ||||
|             line-height: inherit; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &:hover { | ||||
|         .mx_PinnedEventTile_unpinButton { | ||||
|             visibility: visible; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ limitations under the License. | |||
| 
 | ||||
| import React, {useCallback, useContext, useEffect, useState} from "react"; | ||||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { RoomState } from "matrix-js-sdk/src/models/room-state"; | ||||
| import { MatrixEvent } from "matrix-js-sdk/src/models/event"; | ||||
| import { EventType } from 'matrix-js-sdk/src/@types/event'; | ||||
| 
 | ||||
|  | @ -42,7 +43,7 @@ export const usePinnedEvents = (room: Room): string[] => { | |||
|         setPinnedEvents(room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || []); | ||||
|     }, [room]); | ||||
| 
 | ||||
|     useEventEmitter(room.currentState, "RoomState.events", update); | ||||
|     useEventEmitter(room?.currentState, "RoomState.events", update); | ||||
|     useEffect(() => { | ||||
|         update(); | ||||
|         return () => { | ||||
|  | @ -53,7 +54,6 @@ export const usePinnedEvents = (room: Room): string[] => { | |||
| }; | ||||
| 
 | ||||
| const ReadPinsEventId = "im.vector.room.read_pins"; | ||||
| const ReadPinsNumIds = 10; | ||||
| 
 | ||||
| export const useReadPinnedEvents = (room: Room): Set<string> => { | ||||
|     const [readPinnedEvents, setReadPinnedEvents] = useState<Set<string>>(new Set()); | ||||
|  | @ -75,20 +75,36 @@ export const useReadPinnedEvents = (room: Room): Set<string> => { | |||
|     return readPinnedEvents; | ||||
| }; | ||||
| 
 | ||||
| const useRoomState = <T extends any>(room: Room, mapper: (state: RoomState) => T): T => { | ||||
|     const [value, setValue] = useState<T>(room ? mapper(room.currentState) : undefined); | ||||
| 
 | ||||
|     const update = useCallback(() => { | ||||
|         if (!room) return; | ||||
|         setValue(mapper(room.currentState)); | ||||
|     }, [room, mapper]); | ||||
| 
 | ||||
|     useEventEmitter(room?.currentState, "RoomState.events", update); | ||||
|     useEffect(() => { | ||||
|         update(); | ||||
|         return () => { | ||||
|             setValue(undefined); | ||||
|         }; | ||||
|     }, [update]); | ||||
|     return value; | ||||
| }; | ||||
| 
 | ||||
| const PinnedMessagesCard = ({ room, onClose }: IProps) => { | ||||
|     const cli = useContext(MatrixClientContext); | ||||
|     const canUnpin = useRoomState(room, state => state.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli)); | ||||
|     const pinnedEventIds = usePinnedEvents(room); | ||||
|     const readPinnedEvents = useReadPinnedEvents(room); | ||||
| 
 | ||||
|     useEffect(() => { | ||||
|         const newlyRead = pinnedEventIds.filter(id => !readPinnedEvents.has(id)); | ||||
|         if (newlyRead.length > 0) { | ||||
|             // Only keep the last N event IDs to avoid infinite growth
 | ||||
|             // clear out any read pinned events which no longer are pinned
 | ||||
|             cli.setRoomAccountData(room.roomId, ReadPinsEventId, { | ||||
|                 event_ids: [ | ||||
|                     ...newlyRead.reverse(), | ||||
|                     ...readPinnedEvents, | ||||
|                 ].splice(0, ReadPinsNumIds), | ||||
|                 event_ids: pinnedEventIds, | ||||
|             }); | ||||
|         } | ||||
|     }, [cli, room.roomId, pinnedEventIds, readPinnedEvents]); | ||||
|  | @ -122,24 +138,35 @@ const PinnedMessagesCard = ({ room, onClose }: IProps) => { | |||
|     if (!pinnedEvents) { | ||||
|         content = <Spinner />; | ||||
|     } else if (pinnedEvents.length > 0) { | ||||
|         content = pinnedEvents.filter(Boolean).map(ev => ( | ||||
|             <PinnedEventTile | ||||
|                 key={ev.getId()} | ||||
|                 mxRoom={room} | ||||
|                 mxEvent={ev} | ||||
|                 onUnpinned={() => {}} | ||||
|             /> | ||||
|         let onUnpinClicked; | ||||
|         if (canUnpin) { | ||||
|             onUnpinClicked = async (event: MatrixEvent) => { | ||||
|                 const pinnedEvents = room.currentState.getStateEvents(EventType.RoomPinnedEvents, ""); | ||||
|                 if (pinnedEvents?.getContent()?.pinned) { | ||||
|                     const pinned = pinnedEvents.getContent().pinned; | ||||
|                     const index = pinned.indexOf(event.getId()); | ||||
|                     if (index !== -1) { | ||||
|                         pinned.splice(index, 1); | ||||
|                         await cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned }, ""); | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         // show them in reverse, with latest pinned at the top
 | ||||
|         content = pinnedEvents.filter(Boolean).reverse().map(ev => ( | ||||
|             <PinnedEventTile key={ev.getId()} room={room} event={ev} onUnpinClicked={onUnpinClicked} /> | ||||
|         )); | ||||
|     } else { | ||||
|         content = <div className="mx_RightPanel_empty mx_NotificationPanel_empty"> | ||||
|         content = <div className="mx_RightPanel_empty mx_PinnedMessagesCard_empty"> | ||||
|             <h2>{_t("You’re all caught up")}</h2> | ||||
|             <p>{_t("You have no visible notifications.")}</p> | ||||
|         </div>; | ||||
|     } | ||||
| 
 | ||||
|     return <BaseCard | ||||
|         header={<h2>{ _t("Pinned") }</h2>} | ||||
|         className="mx_NotificationPanel" | ||||
|         header={<h2>{ _t("Pinned messages") }</h2>} | ||||
|         className="mx_PinnedMessagesCard" | ||||
|         onClose={onClose} | ||||
|     > | ||||
|         { content } | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => { | |||
| 
 | ||||
|     return <HeaderButton | ||||
|         name="pinnedMessagesButton" | ||||
|         title={_t("Pinned Messages")} | ||||
|         title={_t("Pinned messages")} | ||||
|         isHighlighted={isHighlighted} | ||||
|         onClick={onClick} | ||||
|         analytics={["Right Panel", "Pinned Messages Button", "click"]} | ||||
|  |  | |||
|  | @ -19,110 +19,86 @@ import React from "react"; | |||
| import { Room } from "matrix-js-sdk/src/models/room"; | ||||
| import { MatrixEvent } from "matrix-js-sdk/src/models/event"; | ||||
| 
 | ||||
| import {MatrixClientPeg} from "../../../MatrixClientPeg"; | ||||
| import dis from "../../../dispatcher/dispatcher"; | ||||
| import AccessibleButton from "../elements/AccessibleButton"; | ||||
| import MessageEvent from "../messages/MessageEvent"; | ||||
| import MemberAvatar from "../avatars/MemberAvatar"; | ||||
| import { _t } from '../../../languageHandler'; | ||||
| import {formatFullDate} from '../../../DateUtils'; | ||||
| import {replaceableComponent} from "../../../utils/replaceableComponent"; | ||||
| import { formatDate } from '../../../DateUtils'; | ||||
| import { replaceableComponent } from "../../../utils/replaceableComponent"; | ||||
| import MatrixClientContext from "../../../contexts/MatrixClientContext"; | ||||
| import { getUserNameColorClass } from "../../../utils/FormattingUtils"; | ||||
| import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; | ||||
| 
 | ||||
| interface IProps { | ||||
|     mxRoom: Room; | ||||
|     mxEvent: MatrixEvent; | ||||
|     onUnpinned?(): void; | ||||
|     room: Room; | ||||
|     event: MatrixEvent; | ||||
|     onUnpinClicked?(): void; | ||||
| } | ||||
| 
 | ||||
| const AVATAR_SIZE = 24; | ||||
| 
 | ||||
| @replaceableComponent("views.rooms.PinnedEventTile") | ||||
| export default class PinnedEventTile extends React.Component<IProps> { | ||||
|     static contextType = MatrixClientContext; | ||||
| 
 | ||||
|     private onTileClicked = () => { | ||||
|         dis.dispatch({ | ||||
|             action: 'view_room', | ||||
|             event_id: this.props.mxEvent.getId(), | ||||
|             event_id: this.props.event.getId(), | ||||
|             highlighted: true, | ||||
|             room_id: this.props.mxEvent.getRoomId(), | ||||
|             room_id: this.props.event.getRoomId(), | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     private onUnpinClicked = () => { | ||||
|         const pinnedEvents = this.props.mxRoom.currentState.getStateEvents("m.room.pinned_events", ""); | ||||
|         if (!pinnedEvents || !pinnedEvents.getContent().pinned) { | ||||
|             // Nothing to do: already unpinned
 | ||||
|             if (this.props.onUnpinned) this.props.onUnpinned(); | ||||
|         } else { | ||||
|             const pinned = pinnedEvents.getContent().pinned; | ||||
|             const index = pinned.indexOf(this.props.mxEvent.getId()); | ||||
|             if (index !== -1) { | ||||
|                 pinned.splice(index, 1); | ||||
|                 MatrixClientPeg.get().sendStateEvent(this.props.mxRoom.roomId, 'm.room.pinned_events', {pinned}, '') | ||||
|                     .then(() => { | ||||
|                         if (this.props.onUnpinned) this.props.onUnpinned(); | ||||
|                     }); | ||||
|             } else if (this.props.onUnpinned) this.props.onUnpinned(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     private canUnpin() { | ||||
|         return this.props.mxRoom.currentState.mayClientSendStateEvent('m.room.pinned_events', MatrixClientPeg.get()); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const sender = this.props.mxEvent.getSender(); | ||||
|         // Get the latest sender profile rather than historical
 | ||||
|         const senderProfile = this.props.mxRoom.getMember(sender); | ||||
|         const avatarSize = 40; | ||||
|         const sender = this.props.event.getSender(); | ||||
|         const senderProfile = this.props.room.getMember(sender); | ||||
| 
 | ||||
|         let unpinButton = null; | ||||
|         if (this.canUnpin()) { | ||||
|         if (this.props.onUnpinClicked) { | ||||
|             unpinButton = ( | ||||
|                 <AccessibleButton onClick={this.onUnpinClicked} className="mx_PinnedEventTile_unpinButton"> | ||||
|                     <img | ||||
|                         src={require("../../../../res/img/cancel-red.svg")} | ||||
|                         width="8" | ||||
|                         height="8" | ||||
|                         alt={_t('Unpin Message')} | ||||
|                         title={_t('Unpin Message')} | ||||
|                     /> | ||||
|                 </AccessibleButton> | ||||
|                 <AccessibleTooltipButton | ||||
|                     onClick={this.props.onUnpinClicked} | ||||
|                     className="mx_PinnedEventTile_unpinButton" | ||||
|                     title={_t("Unpin")} | ||||
|                 /> | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_PinnedEventTile"> | ||||
|                 <div className="mx_PinnedEventTile_actions"> | ||||
|                     <AccessibleButton | ||||
|                         className="mx_PinnedEventTile_gotoButton mx_textButton" | ||||
|                         onClick={this.onTileClicked} | ||||
|                     > | ||||
|                         { _t("Jump to message") } | ||||
|                     </AccessibleButton> | ||||
|                     { unpinButton } | ||||
|                 </div> | ||||
|         return <div className="mx_PinnedEventTile"> | ||||
|             <MemberAvatar | ||||
|                 className="mx_PinnedEventTile_senderAvatar" | ||||
|                 member={senderProfile} | ||||
|                 width={AVATAR_SIZE} | ||||
|                 height={AVATAR_SIZE} | ||||
|                 fallbackUserId={sender} | ||||
|             /> | ||||
| 
 | ||||
|                 <span className="mx_PinnedEventTile_senderAvatar"> | ||||
|                     <MemberAvatar | ||||
|                         member={senderProfile} | ||||
|                         width={avatarSize} | ||||
|                         height={avatarSize} | ||||
|                         fallbackUserId={sender} | ||||
|                     /> | ||||
|                 </span> | ||||
|                 <span className="mx_PinnedEventTile_sender"> | ||||
|                     { senderProfile ? senderProfile.name : sender } | ||||
|                 </span> | ||||
|                 <span className="mx_PinnedEventTile_timestamp"> | ||||
|                     { formatFullDate(new Date(this.props.mxEvent.getTs())) } | ||||
|                 </span> | ||||
|                 <div className="mx_PinnedEventTile_message"> | ||||
|                     <MessageEvent | ||||
|                         mxEvent={this.props.mxEvent} | ||||
|                         className="mx_PinnedEventTile_body" | ||||
|                         maxImageHeight={150} | ||||
|                         onHeightChanged={() => {}} // we need to give this, apparently
 | ||||
|                     /> | ||||
|                 </div> | ||||
|             <span className={"mx_PinnedEventTile_sender " + getUserNameColorClass(sender)}> | ||||
|                 { senderProfile?.name || sender } | ||||
|             </span> | ||||
| 
 | ||||
|             { unpinButton } | ||||
| 
 | ||||
|             <div className="mx_PinnedEventTile_message"> | ||||
|                 <MessageEvent | ||||
|                     mxEvent={this.props.event} | ||||
|                     className="mx_PinnedEventTile_body" | ||||
|                     maxImageHeight={150} | ||||
|                     onHeightChanged={() => {}} // we need to give this, apparently
 | ||||
|                 /> | ||||
|             </div> | ||||
|         ); | ||||
| 
 | ||||
|             <div className="mx_PinnedEventTile_footer"> | ||||
|                 <span className="mx_PinnedEventTile_timestamp"> | ||||
|                     { formatDate(new Date(this.props.event.getTs())) } | ||||
|                 </span> | ||||
| 
 | ||||
|                 <AccessibleButton onClick={this.onTileClicked} kind="link"> | ||||
|                     { _t("View message") } | ||||
|                 </AccessibleButton> | ||||
|             </div> | ||||
|         </div>; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1509,7 +1509,7 @@ | |||
|     "Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.", | ||||
|     "This is the start of <roomName/>.": "This is the start of <roomName/>.", | ||||
|     "Unpin Message": "Unpin Message", | ||||
|     "Jump to message": "Jump to message", | ||||
|     "View message": "View message", | ||||
|     "%(duration)ss": "%(duration)ss", | ||||
|     "%(duration)sm": "%(duration)sm", | ||||
|     "%(duration)sh": "%(duration)sh", | ||||
|  | @ -1717,8 +1717,7 @@ | |||
|     "Yours, or the other users’ session": "Yours, or the other users’ session", | ||||
|     "You’re all caught up": "You’re all caught up", | ||||
|     "You have no visible notifications.": "You have no visible notifications.", | ||||
|     "Pinned": "Pinned", | ||||
|     "Pinned Messages": "Pinned Messages", | ||||
|     "Pinned messages": "Pinned messages", | ||||
|     "Room Info": "Room Info", | ||||
|     "You can only pin up to %(count)s widgets|other": "You can only pin up to %(count)s widgets", | ||||
|     "Unpin": "Unpin", | ||||
|  | @ -1952,7 +1951,6 @@ | |||
|     "Rotate Right": "Rotate Right", | ||||
|     "Download": "Download", | ||||
|     "Information": "Information", | ||||
|     "View message": "View message", | ||||
|     "Language Dropdown": "Language Dropdown", | ||||
|     "%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s", | ||||
|     "%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)sjoined %(count)s times", | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ export enum RightPanelPhases { | |||
| export const RIGHT_PANEL_PHASES_NO_ARGS = [ | ||||
|     RightPanelPhases.RoomSummary, | ||||
|     RightPanelPhases.NotificationPanel, | ||||
|     RightPanelPhases.PinnedMessages, | ||||
|     RightPanelPhases.FilePanel, | ||||
|     RightPanelPhases.RoomMemberList, | ||||
|     RightPanelPhases.GroupMemberList, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski