mirror of
https://github.com/element-hq/element-web.git
synced 2024-12-05 05:38:41 +03:00
bc0bfa6377
* PSFD-455: Iterate on search results for message bubbles Though not perfect, this is a bit better than it was before. Specifically, we ensure our matching for `isOwnEvent` correctly identifies the user and that the results don't look *awful*. There's still room for improvement, but this should be a measurable improvement itself. Most notably, this doesn't fix a couple obvious bugs due to complexity: * Message bubbles have the wrong corners for the display format (pre-existing). * The sender's own messages are missing avatars/names. This is just way too complex to fix. **Requires https://github.com/matrix-org/matrix-js-sdk/pull/2004** * Tweak opacity Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
657 lines
17 KiB
SCSS
657 lines
17 KiB
SCSS
/*
|
||
Copyright 2021 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.
|
||
*/
|
||
|
||
.mx_RoomView_body[data-layout=bubble] {
|
||
.mx_RoomView_timeline, .mx_RoomView_statusArea, .mx_MessageComposer {
|
||
width: 100%;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile[data-layout=bubble],
|
||
.mx_GenericEventListSummary[data-layout=bubble] {
|
||
--avatarSize: 32px;
|
||
--gutterSize: 11px;
|
||
--cornerRadius: 12px;
|
||
--maxWidth: 70%;
|
||
margin-right: 60px;
|
||
}
|
||
|
||
.mx_RoomView_searchResultsPanel {
|
||
.mx_EventTile[data-layout=bubble] {
|
||
.mx_SenderProfile {
|
||
// Group layout adds a 64px left margin, which we really don't want on search results
|
||
margin-left: 0;
|
||
}
|
||
|
||
&[data-self=true] {
|
||
// The avatars end up overlapping, so just hide them
|
||
.mx_EventTile_avatar {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
// Mirror rough designs for "greyed out" text
|
||
&.mx_EventTile_contextual .mx_EventTile_line {
|
||
opacity: 0.4;
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_EventTile[data-layout=bubble] {
|
||
position: relative;
|
||
margin-top: var(--gutterSize);
|
||
margin-left: 49px;
|
||
font-size: $font-14px;
|
||
|
||
.mx_ThreadSummary {
|
||
clear: both;
|
||
width: fit-content;
|
||
}
|
||
|
||
.mx_EventTile_content {
|
||
margin-right: 0;
|
||
}
|
||
|
||
&.mx_EventTile_continuation {
|
||
margin-top: 2px;
|
||
}
|
||
|
||
&.mx_EventTile_highlight {
|
||
&::before {
|
||
background-color: $event-highlight-bg-color;
|
||
}
|
||
|
||
color: $alert;
|
||
}
|
||
|
||
/* For replies */
|
||
.mx_EventTile {
|
||
padding-top: 0;
|
||
}
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -1px;
|
||
bottom: -1px;
|
||
left: -60px;
|
||
right: -60px;
|
||
z-index: -1;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
&:hover,
|
||
&.mx_EventTile_selected {
|
||
|
||
&::before {
|
||
background: $eventbubble-bg-hover;
|
||
}
|
||
|
||
.mx_EventTile_avatar {
|
||
img {
|
||
box-shadow: 0 0 0 3px $eventbubble-bg-hover;
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_DisambiguatedProfile,
|
||
.mx_EventTile_line {
|
||
width: fit-content;
|
||
max-width: 70%;
|
||
// fixed line height to prevent emoji from being taller than text
|
||
line-height: $font-18px;
|
||
}
|
||
|
||
// inside mx_RoomView_MessageList, outside of mx_ReplyTile
|
||
// (on the main panel and the chat panel with a maximized widget)
|
||
> .mx_DisambiguatedProfile,
|
||
// inside a thread, outside of mx_ReplyTile
|
||
.mx_EventTile_senderDetails > .mx_DisambiguatedProfile {
|
||
position: relative;
|
||
top: -2px;
|
||
left: 2px;
|
||
font-size: $font-15px;
|
||
}
|
||
|
||
.mx_MessageActionBar {
|
||
top: -28px;
|
||
right: 0;
|
||
z-index: 9; // above the avatar
|
||
}
|
||
|
||
.mx_MediaBody {
|
||
// leave space for the timestamp
|
||
padding-right: 48px;
|
||
}
|
||
|
||
.mx_MImageBody_thumbnail_container {
|
||
min-height: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
|
||
min-width: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
|
||
}
|
||
|
||
.mx_CallEvent {
|
||
background-color: unset;
|
||
|
||
border-style: solid;
|
||
border-width: 1px;
|
||
border-color: $quinary-content;
|
||
}
|
||
|
||
&[data-self=false] {
|
||
.mx_EventTile_line {
|
||
border-bottom-right-radius: var(--cornerRadius);
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-bottom-right-radius: var(--cornerRadius) !important;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile_avatar {
|
||
left: -34px;
|
||
}
|
||
|
||
.mx_MessageActionBar {
|
||
right: -100px; // to make sure it doesn't overflow to the left or cover sender profile
|
||
}
|
||
|
||
.mx_ThreadSummary {
|
||
margin-inline-start: calc(-1 * var(--gutterSize));
|
||
margin-inline-end: auto;
|
||
}
|
||
|
||
--backgroundColor: $eventbubble-others-bg;
|
||
}
|
||
|
||
&[data-self=true] {
|
||
.mx_EventTile_line {
|
||
margin-inline-start: auto;
|
||
border-bottom-left-radius: var(--cornerRadius);
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-bottom-left-radius: var(--cornerRadius) !important;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile_sticker {
|
||
// align timestamp with those inside bubbles
|
||
margin-right: 32px;
|
||
}
|
||
|
||
.mx_ThreadSummary {
|
||
margin-inline-start: auto;
|
||
margin-inline-end: calc(-1 * var(--gutterSize));
|
||
}
|
||
|
||
.mx_DisambiguatedProfile {
|
||
display: none;
|
||
}
|
||
|
||
.mx_ReplyTile .mx_DisambiguatedProfile {
|
||
display: block;
|
||
}
|
||
|
||
.mx_ReactionsRow {
|
||
float: right;
|
||
clear: right;
|
||
display: flex;
|
||
|
||
/* Moving the "add reaction button" before the reactions */
|
||
> :last-child {
|
||
order: -1;
|
||
}
|
||
}
|
||
.mx_EventTile_avatar {
|
||
top: -19px; // height of the sender block
|
||
right: -35px;
|
||
}
|
||
|
||
.mx_MediaBody {
|
||
background: $eventbubble-self-bg;
|
||
}
|
||
|
||
--backgroundColor: $eventbubble-self-bg;
|
||
}
|
||
|
||
.mx_EventTile_line {
|
||
position: relative;
|
||
display: flex;
|
||
gap: 5px;
|
||
margin: 0 -12px 0 -9px;
|
||
border-top-left-radius: var(--cornerRadius);
|
||
border-top-right-radius: var(--cornerRadius);
|
||
|
||
// the selector here is quite weird because timestamps can appear linked & unlinked and in different places
|
||
// in the DOM depending on the specific rendering context
|
||
> a, // timestamp wrapper anchor
|
||
.mx_MessageActionBar + .mx_MessageTimestamp {
|
||
position: absolute;
|
||
padding: 4px 8px;
|
||
bottom: 0;
|
||
right: 0;
|
||
z-index: 3; // above media and location share maps
|
||
}
|
||
|
||
&.mx_EventTile_mediaLine .mx_MVoiceMessageBody {
|
||
// allow the event to be collapsed, this causes the waveform to get cropped
|
||
min-width: 0;
|
||
}
|
||
|
||
// we put the timestamps for media (other than stickers) atop the media
|
||
// for images we also apply a linear gradient and change the timestamp colour to aid readability
|
||
&.mx_EventTile_mediaLine.mx_EventTile_image {
|
||
.mx_MessageTimestamp {
|
||
color: #ffffff; // regardless of theme, always visible on the below gradient
|
||
}
|
||
|
||
// linear gradient to make the timestamp more visible
|
||
.mx_MImageBody::before {
|
||
content: "";
|
||
position: absolute;
|
||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.2) 100%);
|
||
z-index: 1;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
|
||
&.mx_EventTile_sticker {
|
||
> a, // timestamp wrapper anchor
|
||
.mx_MessageActionBar + .mx_MessageTimestamp {
|
||
// position timestamps for stickers to the right of the un-bubbled sticker
|
||
right: unset;
|
||
left: 100%;
|
||
}
|
||
|
||
.mx_MStickerBody_wrapper {
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
.mx_MImageBody {
|
||
width: 100%;
|
||
height: 100%;
|
||
|
||
.mx_MImageBody_thumbnail.mx_MImageBody_thumbnail--blurhash {
|
||
position: unset;
|
||
}
|
||
}
|
||
|
||
//noinspection CssReplaceWithShorthandSafely
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MediaBody {
|
||
border-radius: unset;
|
||
border-top-left-radius: var(--cornerRadius);
|
||
border-top-right-radius: var(--cornerRadius);
|
||
}
|
||
|
||
.mx_EventTile_e2eIcon {
|
||
flex-shrink: 0; // keep it at full size
|
||
}
|
||
}
|
||
|
||
&:not(.mx_EventTile_noBubble) .mx_EventTile_line:not(.mx_EventTile_mediaLine) {
|
||
// make the top and bottom padding 1px smaller so that we can pad .mx_EventTile_content by 1px
|
||
// to avoid anti-zalgo cutting off our larger than text emojis.
|
||
padding: calc(var(--gutterSize) - 1px);
|
||
padding-right: 60px; // space for the timestamp
|
||
background: var(--backgroundColor);
|
||
|
||
.mx_EventTile_content {
|
||
padding: 1px;
|
||
}
|
||
}
|
||
|
||
&.mx_EventTile_continuation[data-self=false] .mx_EventTile_line {
|
||
border-top-left-radius: 0;
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-top-left-radius: 0;
|
||
}
|
||
}
|
||
&.mx_EventTile_lastInSection[data-self=false] .mx_EventTile_line {
|
||
border-bottom-left-radius: var(--cornerRadius);
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-bottom-left-radius: var(--cornerRadius);
|
||
}
|
||
}
|
||
|
||
&.mx_EventTile_continuation[data-self=true] .mx_EventTile_line {
|
||
border-top-right-radius: 0;
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-top-right-radius: 0;
|
||
}
|
||
}
|
||
&.mx_EventTile_lastInSection[data-self=true] .mx_EventTile_line {
|
||
border-bottom-right-radius: var(--cornerRadius);
|
||
|
||
.mx_MImageBody .mx_MImageBody_thumbnail_container,
|
||
.mx_MVideoBody .mx_MVideoBody_container,
|
||
.mx_MImageBody::before,
|
||
.mx_MediaBody,
|
||
.mx_MLocationBody_map,
|
||
.mx_MBeaconBody {
|
||
border-bottom-right-radius: var(--cornerRadius);
|
||
}
|
||
}
|
||
|
||
.mx_EventTile_avatar {
|
||
position: absolute;
|
||
top: 0;
|
||
line-height: 1;
|
||
z-index: 9;
|
||
img {
|
||
box-shadow: 0 0 0 3px $background;
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
|
||
&.mx_EventTile_noSender {
|
||
.mx_EventTile_avatar {
|
||
top: -19px;
|
||
}
|
||
}
|
||
|
||
.mx_BaseAvatar,
|
||
.mx_EventTile_avatar {
|
||
line-height: 1;
|
||
}
|
||
|
||
&[data-has-reply=true] {
|
||
> .mx_EventTile_line {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.mx_ReplyChain_show {
|
||
order: 99999;
|
||
}
|
||
|
||
.mx_ReplyChain {
|
||
.mx_EventTile_reply {
|
||
max-width: 90%;
|
||
padding: 0;
|
||
> a, // timestamp wrapper anchor
|
||
.mx_MessageActionBar + .mx_MessageTimestamp {
|
||
display: none !important;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile {
|
||
display: flex;
|
||
gap: var(--gutterSize);
|
||
.mx_EventTile_avatar {
|
||
position: static;
|
||
}
|
||
.mx_DisambiguatedProfile {
|
||
display: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_MPollBody {
|
||
width: 550px; // to prevent timestamp overlapping summary text
|
||
max-width: 100%; // prevent overflowing a reply tile
|
||
|
||
.mx_MPollBody_totalVotes {
|
||
// align summary text with corner timestamp
|
||
padding: 4px 0;
|
||
}
|
||
}
|
||
|
||
.mx_ReactionsRow {
|
||
margin-right: -18px;
|
||
margin-left: -9px;
|
||
}
|
||
|
||
/* Special layout scenario for "Unable To Decrypt (UTD)" events */
|
||
&.mx_EventTile_bad > .mx_EventTile_line {
|
||
display: grid;
|
||
grid-template:
|
||
"reply reply" auto
|
||
"shield body" auto
|
||
"shield link" auto
|
||
/ auto 1fr;
|
||
.mx_EventTile_e2eIcon {
|
||
grid-area: shield;
|
||
}
|
||
.mx_UnknownBody {
|
||
grid-area: body;
|
||
}
|
||
.mx_EventTile_keyRequestInfo {
|
||
grid-area: link;
|
||
}
|
||
.mx_ReplyChain_wrapper {
|
||
grid-area: reply;
|
||
}
|
||
}
|
||
|
||
.mx_ReadReceiptGroup {
|
||
position: absolute;
|
||
// as close to right gutter without clipping as possible
|
||
right: -78px;
|
||
// (EventTileLine.line-height - ReadReceiptGroup.height) / 2
|
||
// this centers the ReadReceiptGroup if we’ve got a single line
|
||
bottom: calc(($font-18px - 24px) / 2);
|
||
top: auto;
|
||
}
|
||
|
||
.mx_MTextBody {
|
||
max-width: 100%;
|
||
}
|
||
|
||
.mx_CallEvent_wrapper {
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile.mx_EventTile_noBubble[data-layout=bubble] {
|
||
--backgroundColor: transparent;
|
||
|
||
.mx_EventTile_line.mx_EventTile_emote {
|
||
padding-right: 60px; // align with bubbles text
|
||
font-style: italic;
|
||
|
||
> a { // timestamp anchor wrapper
|
||
align-self: center;
|
||
bottom: unset;
|
||
top: unset;
|
||
font-style: normal; // undo italic above
|
||
}
|
||
|
||
.mx_MEmoteBody {
|
||
padding: 4px 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble],
|
||
.mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble] {
|
||
.mx_EventTile_line > a {
|
||
// hide this timestamp as the tile will render its own
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble],
|
||
.mx_EventTile.mx_EventTile_leftAlignedBubble[data-layout=bubble],
|
||
.mx_EventTile.mx_EventTile_info[data-layout=bubble],
|
||
.mx_GenericEventListSummary[data-layout=bubble][data-expanded=false] {
|
||
--backgroundColor: transparent;
|
||
--gutterSize: 0;
|
||
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
padding: 5px 0;
|
||
|
||
.mx_EventTile_avatar {
|
||
position: static;
|
||
order: -1;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.mx_EventTile_line,
|
||
.mx_EventTile_info {
|
||
min-width: 100%;
|
||
// Preserve alignment with left edge of text in bubbles
|
||
margin: 0;
|
||
}
|
||
|
||
.mx_EventTile_e2eIcon {
|
||
margin-left: 9px;
|
||
}
|
||
|
||
.mx_EventTile_line {
|
||
> a, // timestamp wrapper anchor
|
||
.mx_MessageActionBar + .mx_MessageTimestamp {
|
||
right: auto;
|
||
left: -77px;
|
||
bottom: unset;
|
||
align-self: center;
|
||
|
||
.mx_MessageTimestamp, &.mx_MessageTimestamp {
|
||
vertical-align: middle;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_GenericEventListSummary[data-layout=bubble] {
|
||
--maxWidth: 70%;
|
||
margin-left: calc(var(--avatarSize) + var(--gutterSize));
|
||
|
||
.mx_GenericEventListSummary_toggle {
|
||
margin: 0 55px 0 5px;
|
||
float: none;
|
||
|
||
&[aria-expanded=false] {
|
||
order: 9;
|
||
}
|
||
&[aria-expanded=true] {
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.mx_GenericEventListSummary_line {
|
||
display: none;
|
||
}
|
||
|
||
.mx_GenericEventListSummary_avatars {
|
||
padding-top: 0;
|
||
}
|
||
|
||
.mx_EventTile {
|
||
&.mx_EventTile_info {
|
||
.mx_EventTile_line {
|
||
// Avoid overflow of event info by cancelling width settings
|
||
width: 100%;
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
&::after {
|
||
content: "";
|
||
clear: both;
|
||
}
|
||
}
|
||
|
||
.mx_GenericEventListSummary[data-expanded=false][data-layout=bubble] {
|
||
// Align with left edge of bubble tiles
|
||
padding: 0 49px;
|
||
}
|
||
|
||
// ideally we'd use display=contents here for the layout to all work regardless of the *ELS but
|
||
// that breaks ScrollPanel's reliance upon offsetTop so we have to have a bit more finesse.
|
||
.mx_GenericEventListSummary[data-expanded=true][data-layout=bubble] {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin: 0;
|
||
|
||
.mx_EventTile_info {
|
||
padding: 2px 0;
|
||
margin-right: 0;
|
||
|
||
.mx_MessageActionBar {
|
||
right: 48px; // align with that of right-column bubbles
|
||
}
|
||
|
||
.mx_ReadReceiptGroup {
|
||
right: -18px; // match alignment to RRs of chat bubbles
|
||
}
|
||
|
||
&::before {
|
||
right: 0; // match alignment of the hover background to that of chat bubbles
|
||
}
|
||
}
|
||
}
|
||
|
||
// increase margin between ELS and the next Event to not have our user avatar overlap the expand/collapse button
|
||
.mx_GenericEventListSummary[data-layout=bubble][data-expanded=false] + .mx_EventTile[data-layout=bubble][data-self=true] {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* events that do not require bubble layout */
|
||
.mx_GenericEventListSummary[data-layout=bubble],
|
||
.mx_EventTile.mx_EventTile_bad[data-layout=bubble] {
|
||
.mx_EventTile_line {
|
||
background: transparent;
|
||
}
|
||
|
||
&:hover {
|
||
&::before {
|
||
background: transparent;
|
||
}
|
||
}
|
||
}
|
||
|
||
.mx_EventTile_tileError[data-layout=bubble] .mx_EventTile_line {
|
||
flex-direction: column; // restore the centering
|
||
}
|