Enable threads by default and mark it as a beta feature (#8081)

This commit is contained in:
Germain 2022-04-05 17:15:31 +01:00 committed by GitHub
parent 27e48062b6
commit 694c39e72d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 257 additions and 68 deletions

View file

@ -103,6 +103,7 @@ $pulse-color: $alert;
mask-position: center;
}
}
.mx_RightPanel_headerButton_unreadIndicator_bg {
position: absolute;
right: $dot-offset;
@ -121,14 +122,6 @@ $pulse-color: $alert;
right: $dot-offset;
top: $dot-offset;
margin: 4px;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_RightPanel_indicator_pulse 2s infinite;
animation-iteration-count: 1;
&.mx_Indicator_red {
background: rgba($alert, 1);
@ -144,22 +137,6 @@ $pulse-color: $alert;
background: rgba($primary-content, 1);
box-shadow: rgba($primary-content, 1);
}
&::after {
content: "";
position: absolute;
width: inherit;
height: inherit;
top: 0;
left: 0;
transform: scale(1);
transform-origin: center center;
animation-name: mx_RightPanel_indicator_pulse_shadow;
animation-duration: inherit;
animation-iteration-count: inherit;
border-radius: 50%;
background: inherit;
}
}
.mx_RightPanel_timelineCardButton {
@ -250,7 +227,8 @@ $pulse-color: $alert;
margin: 16px 0;
}
h2, p {
h2,
p {
font-size: $font-14px;
}

View file

@ -105,7 +105,9 @@ limitations under the License.
flex: 1;
min-width: 0;
.mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner {
.mx_RoomView_messagePanel,
.mx_RoomView_messagePanelSpinner,
.mx_RoomView_messagePanelSearchSpinner {
order: 2;
}
}
@ -147,20 +149,17 @@ limitations under the License.
}
.mx_RoomView_messageListWrapper {
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
position: relative;
}
.mx_RoomView_searchResultsPanel {
.mx_RoomView_messageListWrapper {
justify-content: flex-start;
> .mx_RoomView_MessageList > li > ol {
>.mx_RoomView_MessageList > li > ol {
list-style-type: none;
}
}
@ -295,3 +294,62 @@ hr.mx_RoomView_myReadMarker {
min-height: 42px;
}
}
@keyframes mx_Indicator_pulse {
0% {
transform: scale(0.95);
}
70% {
transform: scale(1);
}
100% {
transform: scale(0.95);
}
}
@keyframes mx_Indicator_pulse_shadow {
0% {
opacity: 0.7;
}
70% {
transform: scale(2.2);
opacity: 0;
}
100% {
opacity: 0;
}
}
.mx_Indicator {
position: absolute;
right: 0;
top: 0;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_Indicator_pulse 2s infinite;
animation-iteration-count: 1;
&::after {
content: "";
position: absolute;
width: inherit;
height: inherit;
top: 0;
left: 0;
transform: scale(1);
transform-origin: center center;
animation-name: mx_Indicator_pulse_shadow;
animation-duration: inherit;
animation-iteration-count: inherit;
border-radius: 50%;
background: inherit;
}
}

View file

@ -16,13 +16,23 @@ limitations under the License.
*/
@keyframes mx_fadein {
from { opacity: 0; }
to { opacity: 1; }
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes mx_fadeout {
from { opacity: 1; }
to { opacity: 0; }
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.mx_Tooltip_chevron {

View file

@ -48,7 +48,7 @@ limitations under the License.
cursor: initial;
}
> * {
>* {
white-space: nowrap;
display: inline-block;
position: relative;
@ -102,6 +102,11 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/message/thread.svg');
}
.mx_MessageActionBar_threadButton .mx_Indicator {
background: $links;
animation-iteration-count: infinite;
}
.mx_MessageActionBar_editButton::after {
mask-image: url('$(res)/img/element-icons/room/message-bar/edit.svg');
}

View file

@ -17,6 +17,8 @@ limitations under the License.
.mx_ThreadPanel {
display: flex;
flex-direction: column;
height: 100px;
overflow: visible;
.mx_BaseCard_header {
margin-bottom: 12px;
@ -225,6 +227,20 @@ limitations under the License.
display: none; // hide the hidden event expand button, not enough space, view source can still be used
}
}
.mx_BaseCard_footer {
text-align: left;
font-size: $font-12px;
align-items: center;
justify-content: end;
gap: 4px;
position: relative;
top: 2px;
.mx_AccessibleButton_kind_link_inline {
color: $secondary-content;
}
}
}
.mx_ThreadPanel_replies {
@ -269,10 +285,10 @@ limitations under the License.
align-items: center;
justify-content: center;
position: absolute;
top: 48px;
bottom: 8px;
left: 8px;
right: 8px;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
h2 {

View file

@ -940,6 +940,7 @@ $threadInfoLineHeight: calc(2 * $font-12px);
.mx_ThreadView {
display: flex;
flex-direction: column;
max-height: 100%;
.mx_ThreadView_List {
flex: 1;

BIN
res/img/betas/threads.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View file

@ -8,9 +8,22 @@
/* Noto Color Emoji contains digits, in fixed-width, therefore causing
digits in flowed text to stand out.
TODO: Consider putting all emoji fonts to the end rather than the front. */
$font-family: 'Inter', 'Twemoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Arial', 'Helvetica', sans-serif, 'Noto Color Emoji';
$font-family: 'Inter',
'Twemoji',
'Apple Color Emoji',
'Segoe UI Emoji',
'Arial',
'Helvetica',
sans-serif,
'Noto Color Emoji';
$monospace-font-family: 'Inconsolata', 'Twemoji', 'Apple Color Emoji', 'Segoe UI Emoji', 'Courier', monospace, 'Noto Color Emoji';
$monospace-font-family: 'Inconsolata',
'Twemoji',
'Apple Color Emoji',
'Segoe UI Emoji',
'Courier',
monospace,
'Noto Color Emoji';
// Colors from Figma Compound https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=559%3A120
// ********************
@ -57,7 +70,7 @@ $icon-button-color: $quaternary-content;
// Colors that aren't in Figma and are theme specific - we need to get rid of these
// ********************
$selection-fg-color: $background;
$yellow-background: #fff8e3;
$yellow-background: #fff8e3;
$secondary-accent-color: #f2f5f8;
$button-fg-color: $background;
$neutral-badge-color: #dbdbdb;
@ -79,7 +92,8 @@ $event-selected-color: #f6f7f8;
$topleftmenu-color: #212121;
$roomtopic-color: #9e9e9e;
$spacePanel-bg-color: rgba(232, 232, 232, 0.77);
$panel-gradient: rgba(242, 245, 248, 0), rgba(242, 245, 248, 1);
$panel-gradient: rgba(242, 245, 248, 0),
rgba(242, 245, 248, 1);
$h3-color: #3d3b39;
$event-highlight-bg-color: $yellow-background;
$header-panel-text-primary-color: #91A1C0;
@ -296,6 +310,7 @@ $focus-brightness: 105%;
:root {
--lp-background-blur: 40px;
}
// ********************
// Icon URLs
@ -330,8 +345,7 @@ $location-live-secondary-color: #deddfd;
outline: none;
}
@define-mixin mx_DialogButton_hover {
}
@define-mixin mx_DialogButton_hover {}
@define-mixin mx_DialogButton_danger {
background-color: $accent;
@ -355,6 +369,7 @@ $location-live-secondary-color: #deddfd;
color: $accent;
text-decoration: none;
}
// ********************
// diff highlight colors
@ -366,4 +381,5 @@ $location-live-secondary-color: #deddfd;
.hljs-deletion {
background: #fdd;
}
// ********************

View file

@ -31,7 +31,14 @@ import { Layout } from '../../settings/enums/Layout';
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
import Measured from '../views/elements/Measured';
import PosthogTrackers from "../../PosthogTrackers";
import { ButtonEvent } from "../views/elements/AccessibleButton";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import { BetaPill } from '../views/beta/BetaCard';
import SdkConfig from '../../SdkConfig';
import Modal from '../../Modal';
import BetaFeedbackDialog from '../views/dialogs/BetaFeedbackDialog';
import { Action } from '../../dispatcher/actions';
import { UserTab } from '../views/dialogs/UserSettingsDialog';
import dis from '../../dispatcher/dispatcher';
interface IProps {
roomId: string;
@ -233,6 +240,12 @@ const ThreadPanel: React.FC<IProps> = ({
}
}, [timelineSet, timelinePanel]);
const openFeedback = SdkConfig.get().bug_report_endpoint_url ? () => {
Modal.createTrackedDialog("Threads Feedback", "feature_thread", BetaFeedbackDialog, {
featureId: "feature_thread",
});
} : null;
return (
<RoomContext.Provider value={{
...roomContext,
@ -246,6 +259,22 @@ const ThreadPanel: React.FC<IProps> = ({
setFilterOption={setFilterOption}
empty={threadCount === 0}
/>}
footer={<>
<BetaPill
tooltipTitle={_t("Threads are a beta feature")}
tooltipCaption={_t("Click for more info")}
onClick={() => {
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,
});
}}
/>
{ openFeedback && _t("<a>Give feedback</a>", {}, {
a: sub =>
<AccessibleButton kind="link_inline" onClick={openFeedback}>{ sub }</AccessibleButton>,
}) }
</>}
className="mx_ThreadPanel"
onClose={onClose}
withoutScrollContainer={true}

View file

@ -36,17 +36,27 @@ interface IProps {
featureId: string;
}
export const BetaPill = ({ onClick }: { onClick?: () => void }) => {
interface IBetaPillProps {
onClick?: () => void;
tooltipTitle?: string;
tooltipCaption?: string;
}
export const BetaPill = ({
onClick,
tooltipTitle = _t("This is a beta feature"),
tooltipCaption = _t("Click for more info"),
}: IBetaPillProps) => {
if (onClick) {
return <AccessibleTooltipButton
className="mx_BetaCard_betaPill"
title={_t("This is a beta feature. Click for more info")}
title={`${tooltipTitle} ${tooltipCaption}`}
tooltip={<div>
<div className="mx_Tooltip_title">
{ _t("This is a beta feature") }
{ tooltipTitle }
</div>
<div className="mx_Tooltip_sub">
{ _t("Click for more info") }
{ tooltipCaption }
</div>
</div>}
onClick={onClick}

View file

@ -35,7 +35,7 @@ const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
const info = SettingsStore.getBetaInfo(featureId);
return <GenericFeatureFeedbackDialog
title={_t("%(featureName)s beta feedback", { featureName: info.title })}
title={_t("%(featureName)s Beta feedback", { featureName: info.title })}
subheading={_t(info.feedbackSubheading)}
onFinished={onFinished}
rageshakeLabel={info.feedbackLabel}

View file

@ -44,6 +44,8 @@ import { showThread } from "../../../dispatcher/dispatch-actions/threads";
import { shouldDisplayReply } from '../../../utils/Reply';
import { Key } from "../../../Keyboard";
import { ALTERNATE_KEY_NAME } from "../../../accessibility/KeyboardShortcuts";
import { UserTab } from '../dialogs/UserSettingsDialog';
import { Action } from '../../../dispatcher/actions';
interface IOptionsButtonProps {
mxEvent: MatrixEvent;
@ -223,7 +225,18 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
};
private onThreadClick = (isCard: boolean): void => {
showThread({ rootEvent: this.props.mxEvent, push: isCard });
if (localStorage.getItem("mx_seen_feature_thread") === null) {
localStorage.setItem("mx_seen_feature_thread", "true");
}
if (!SettingsStore.getValue("feature_thread")) {
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,
});
} else {
showThread({ rootEvent: this.props.mxEvent, push: isCard });
}
};
private onEditClick = (): void => {
@ -235,14 +248,13 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
];
private get showReplyInThreadAction(): boolean {
const isThreadEnabled = SettingsStore.getValue("feature_thread");
const inNotThreadTimeline = this.context.timelineRenderingType !== TimelineRenderingType.Thread;
const isAllowedMessageType = !this.forbiddenThreadHeadMsgType.includes(
this.props.mxEvent.getContent().msgtype as MsgType,
);
return isThreadEnabled && inNotThreadTimeline && isAllowedMessageType;
return inNotThreadTimeline && isAllowedMessageType;
}
/**
@ -298,21 +310,42 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
key="cancel"
/>;
const hasARelation = !!this.props.mxEvent?.getRelation()?.rel_type;
const relationType = this.props.mxEvent?.getRelation()?.rel_type;
const hasARelation = !!relationType && relationType !== RelationType.Thread;
const firstTimeSeeingThreads = localStorage.getItem("mx_seen_feature_thread") === null &&
!SettingsStore.getValue("feature_thread");
const threadTooltipButton = <CardContext.Consumer key="thread">
{ context =>
<RovingAccessibleTooltipButton
className="mx_MessageActionBar_maskButton mx_MessageActionBar_threadButton"
disabled={hasARelation}
tooltip={<>
<div className="mx_Tooltip_title">
{ !hasARelation
? _t("Reply in thread")
: _t("Can't create a thread from an event with an existing relation") }
</div>
{ !hasARelation && (
<div className="mx_Tooltip_sub">
{ SettingsStore.getValue("feature_thread")
? _t("Beta feature")
: _t("Beta feature. Click to learn more.")
}
</div>
) }
</>}
title={!hasARelation
? _t("Reply in thread")
: _t("Can't create a thread from an event with an existing relation")
}
: _t("Can't create a thread from an event with an existing relation")}
onClick={this.onThreadClick.bind(null, context.isCard)}
/>
>
{ firstTimeSeeingThreads && (
<div className="mx_Indicator" />
) }
</RovingAccessibleTooltipButton>
}
</CardContext.Consumer>;
@ -387,14 +420,14 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
'mx_MessageActionBar_expandMessageButton': !this.props.isQuoteExpanded,
'mx_MessageActionBar_collapseMessageButton': this.props.isQuoteExpanded,
});
const tooltip = <div>
const tooltip = <>
<div className="mx_Tooltip_title">
{ this.props.isQuoteExpanded ? _t("Collapse quotes") : _t("Expand quotes") }
</div>
<div className="mx_Tooltip_sub">
{ _t(ALTERNATE_KEY_NAME[Key.SHIFT]) + " + " + _t("Click") }
</div>
</div>;
</>;
toolbarOpts.push(<RovingAccessibleTooltipButton
className={expandClassName}
title={this.props.isQuoteExpanded ? _t("Collapse quotes") : _t("Expand quotes")}

View file

@ -61,6 +61,7 @@ const UnreadIndicator = ({ color }: IUnreadIndicatorProps) => {
}
const classes = classNames({
"mx_Indicator": true,
"mx_RightPanel_headerButton_unreadIndicator": true,
"mx_Indicator_bold": color === NotificationColor.Bold,
"mx_Indicator_gray": color === NotificationColor.Grey,

View file

@ -612,7 +612,7 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
* when we are at the sync stage
*/
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const thread = room?.threads.get(this.props.mxEvent.getId());
const thread = room?.threads?.get(this.props.mxEvent.getId());
return thread || null;
}

View file

@ -867,6 +867,13 @@
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Message Pinning": "Message Pinning",
"Threaded messaging": "Threaded messaging",
"Keep discussions organised with threads.": "Keep discussions organised with threads.",
"Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.": "Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.",
"How can I start a thread?": "How can I start a thread?",
"Use \"Reply in thread\" when hovering over a message.": "Use \"Reply in thread\" when hovering over a message.",
"How can I leave the beta?": "How can I leave the beta?",
"To leave, return to this page and use the “Leave the beta” button.": "To leave, return to this page and use the “Leave the beta” button.",
"Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.",
"Custom user status messages": "Custom user status messages",
"Video rooms (under active development)": "Video rooms (under active development)",
"Render simple counters in room header": "Render simple counters in room header",
@ -886,9 +893,7 @@
"This feature is a work in progress, we'd love to hear your feedback.": "This feature is a work in progress, we'd love to hear your feedback.",
"How can I give feedback?": "How can I give feedback?",
"To feedback, join the beta, start a search and click on feedback.": "To feedback, join the beta, start a search and click on feedback.",
"How can I leave the beta?": "How can I leave the beta?",
"To leave, just return to this page or click on the beta badge when you search.": "To leave, just return to this page or click on the beta badge when you search.",
"Thank you for trying the beta, please go into as much detail as you can so we can improve it.": "Thank you for trying the beta, please go into as much detail as you can so we can improve it.",
"Right panel stays open (defaults to room member list)": "Right panel stays open (defaults to room member list)",
"Jump to date (adds /jumptodate and jump to date headers)": "Jump to date (adds /jumptodate and jump to date headers)",
"Don't send read receipts": "Don't send read receipts",
@ -2075,6 +2080,8 @@
"Edit": "Edit",
"Reply in thread": "Reply in thread",
"Can't create a thread from an event with an existing relation": "Can't create a thread from an event with an existing relation",
"Beta feature": "Beta feature",
"Beta feature. Click to learn more.": "Beta feature. Click to learn more.",
"Reply": "Reply",
"Collapse quotes": "Collapse quotes",
"Expand quotes": "Expand quotes",
@ -2371,7 +2378,7 @@
"Invite anyway and never warn me again": "Invite anyway and never warn me again",
"Invite anyway": "Invite anyway",
"Close dialog": "Close dialog",
"%(featureName)s beta feedback": "%(featureName)s beta feedback",
"%(featureName)s Beta feedback": "%(featureName)s Beta feedback",
"To leave the beta, visit your settings.": "To leave the beta, visit your settings.",
"Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Please tell us what went wrong or, better, create a GitHub issue that describes the problem.",
"Preparing to send logs": "Preparing to send logs",
@ -2886,7 +2893,6 @@
"Revoke permissions": "Revoke permissions",
"Move left": "Move left",
"Move right": "Move right",
"This is a beta feature. Click for more info": "This is a beta feature. Click for more info",
"This is a beta feature": "This is a beta feature",
"Click for more info": "Click for more info",
"Beta": "Beta",
@ -3104,6 +3110,8 @@
"Threads help keep your conversations on-topic and easy to track.": "Threads help keep your conversations on-topic and easy to track.",
"<b>Tip:</b> Use \"Reply in thread\" when hovering over a message.": "<b>Tip:</b> Use \"Reply in thread\" when hovering over a message.",
"Keep discussions organised with threads": "Keep discussions organised with threads",
"Threads are a beta feature": "Threads are a beta feature",
"<a>Give feedback</a>": "<a>Give feedback</a>",
"Thread": "Thread",
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.",

View file

@ -165,7 +165,7 @@ export interface IBaseSetting<T extends SettingValueType = SettingValueType> {
title: string; // _td
caption: () => ReactNode;
disclaimer?: (enabled: boolean) => ReactNode;
image: string; // require(...)
image?: string; // require(...)
feedbackSubheading?: string;
feedbackLabel?: string;
extraSettings?: string[];
@ -228,6 +228,30 @@ export const SETTINGS: {[setting: string]: ISetting} = {
displayName: _td("Threaded messaging"),
supportedLevels: LEVELS_FEATURE,
default: false,
betaInfo: {
title: _td("Threads"),
caption: () => <>
<p>{ _t("Keep discussions organised with threads.") }</p>
<p>{ _t("Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.", {}, {
a: (sub) => <a href="https://element.io/help#threads" rel="noreferrer noopener" target="_blank">
{ sub }
</a>,
}) }</p>
</>,
disclaimer: () =>
SdkConfig.get().bug_report_endpoint_url && <>
<h4>{ _t("How can I start a thread?") }</h4>
<p>{ _t("Use \"Reply in thread\" when hovering over a message.") }</p>
<h4>{ _t("How can I leave the beta?") }</h4>
<p>{ _t("To leave, return to this page and use the “Leave the beta” button.") }</p>
</>,
feedbackLabel: "thread-feedback",
feedbackSubheading: _td("Thank you for trying the beta, " +
"please go into as much detail as you can so we can improve it."),
image: require("../../res/img/betas/threads.png"),
requiresRefresh: true,
},
},
"feature_custom_status": {
isFeature: true,