Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 3875020

Browse files
authored
Design thread list tiles according to mockups (#7078)
1 parent 2a20d9a commit 3875020

File tree

8 files changed

+332
-181
lines changed

8 files changed

+332
-181
lines changed

res/css/views/right_panel/_ThreadPanel.scss

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@ limitations under the License.
2121
padding-right: 0;
2222

2323
.mx_BaseCard_header {
24-
padding: 6px 8px 6px 0;
25-
2624
.mx_BaseCard_close,
2725
.mx_BaseCard_back {
2826
margin-top: 15px;
2927
}
30-
3128
.mx_BaseCard_close {
3229
right: -8px;
3330
}
@@ -39,6 +36,7 @@ limitations under the License.
3936
display: flex;
4037
flex: 1;
4138
justify-content: space-between;
39+
align-items: center;
4240

4341
span:first-of-type {
4442
font-weight: 600;
@@ -49,7 +47,11 @@ limitations under the License.
4947

5048
.mx_AccessibleButton {
5149
font-size: 12px;
52-
color: $secondary-content;
50+
color: $primary-content;
51+
}
52+
53+
.mx_MessageActionBar_optionsButton {
54+
position: relative;
5355
}
5456

5557
.mx_ContextualMenu_wrapper {
@@ -178,6 +180,33 @@ limitations under the License.
178180
padding: 0 8px;
179181
box-sizing: border-box;
180182
}
183+
184+
.mx_ThreadPanel_dropdown {
185+
padding: 4px 8px;
186+
border-radius: 4px;
187+
line-height: 1.5;
188+
user-select: none;
189+
}
190+
191+
.mx_ThreadPanel_dropdown:hover,
192+
.mx_ThreadPanel_dropdown[aria-expanded=true] {
193+
background: $quinary-content;
194+
}
195+
196+
.mx_ThreadPanel_dropdown::before {
197+
content: "";
198+
width: 18px;
199+
height: 18px;
200+
background: currentColor;
201+
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
202+
mask-size: 100%;
203+
mask-repeat: no-repeat;
204+
float: right;
205+
}
206+
207+
.mx_ThreadPanel_dropdown[aria-expanded=true]::before {
208+
transform: rotate(180deg);
209+
}
181210
}
182211

183212
.mx_ThreadPanel_viewInRoom::before {

res/css/views/rooms/_EventTile.scss

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -687,28 +687,82 @@ $left-gutter: 64px;
687687
padding-left: 11px;
688688
padding-right: 15px;
689689
}
690+
}
690691

691-
.mx_ThreadInfo_content {
692-
text-overflow: ellipsis;
693-
overflow: hidden;
694-
white-space: nowrap;
695-
padding-left: 8px;
692+
.mx_ThreadInfo_content {
693+
text-overflow: ellipsis;
694+
overflow: hidden;
695+
white-space: nowrap;
696+
padding-left: 8px;
697+
}
698+
699+
.mx_ThreadInfo_thread-icon {
700+
mask-image: url('$(res)/img/element-icons/thread-summary.svg');
701+
mask-position: center;
702+
height: 16px;
703+
min-width: 16px;
704+
background-color: $secondary-content;
705+
mask-repeat: no-repeat;
706+
mask-size: contain;
707+
}
708+
.mx_ThreadInfo_threads-amount {
709+
font-weight: 600;
710+
position: relative;
711+
padding: 0 8px;
712+
white-space: nowrap;
713+
}
714+
715+
.mx_EventTile[data-shape=thread_list] {
716+
--topOffset: 24px;
717+
--leftOffset: 46px;
718+
719+
margin: var(--topOffset) 0;
720+
border-radius: 8px;
721+
722+
&:hover {
723+
background-color: $system;
696724
}
697725

698-
.mx_ThreadInfo_thread-icon {
699-
mask-image: url('$(res)/img/element-icons/thread-summary.svg');
700-
mask-position: center;
701-
height: 16px;
702-
min-width: 16px;
703-
background-color: $secondary-content;
704-
mask-repeat: no-repeat;
705-
mask-size: contain;
726+
&::after {
727+
content: "";
728+
position: absolute;
729+
left: var(--leftOffset);
730+
right: 0;
731+
height: 1px;
732+
bottom: calc(-1 * var(--topOffset));
733+
background-color: $quinary-content;
706734
}
707-
.mx_ThreadInfo_threads-amount {
708-
font-weight: 600;
709-
position: relative;
710-
padding: 0 8px;
711-
white-space: nowrap;
735+
736+
&:last-child {
737+
&::after {
738+
content: unset;
739+
}
740+
margin-bottom: 0;
741+
}
742+
743+
&:first-child {
744+
margin-top: 0;
745+
}
746+
747+
padding-top: 0;
748+
749+
.mx_EventTile_avatar {
750+
top: -4px;
751+
left: 0;
752+
}
753+
754+
.mx_SenderProfile {
755+
margin-left: var(--leftOffset) !important;
756+
}
757+
758+
.mx_EventTile_line {
759+
padding-left: var(--leftOffset) !important;
760+
padding-bottom: 0;
761+
}
762+
.mx_MessageTimestamp {
763+
right: 0;
764+
left: auto;
765+
top: -23px;
712766
}
713767
}
714768

src/components/structures/ThreadPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export const ThreadPanelHeader = ({ filterOption, setFilterOption }: {
167167
</ContextMenu> : null;
168168
return <div className="mx_ThreadPanel__header">
169169
<span>{ _t("Threads") }</span>
170-
<ContextMenuButton inputRef={button} isExpanded={menuDisplayed} onClick={() => menuDisplayed ? closeMenu() : openMenu()}>
170+
<ContextMenuButton className="mx_ThreadPanel_dropdown" inputRef={button} isExpanded={menuDisplayed} onClick={() => menuDisplayed ? closeMenu() : openMenu()}>
171171
{ `${_t('Show:')} ${value.label}` }
172172
</ContextMenuButton>
173173
{ contextMenu }

src/components/structures/ThreadView.tsx

Lines changed: 5 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,8 @@ import EditorStateTransfer from '../../utils/EditorStateTransfer';
3939
import RoomContext, { TimelineRenderingType } from '../../contexts/RoomContext';
4040
import ContentMessages from '../../ContentMessages';
4141
import UploadBar from './UploadBar';
42-
import { ChevronFace, ContextMenuTooltipButton } from './ContextMenu';
4342
import { _t } from '../../languageHandler';
44-
import IconizedContextMenu, {
45-
IconizedContextMenuOption,
46-
IconizedContextMenuOptionList,
47-
} from '../views/context_menus/IconizedContextMenu';
48-
import { ButtonEvent } from '../views/elements/AccessibleButton';
49-
import { copyPlaintext } from '../../utils/strings';
50-
import { sleep } from 'matrix-js-sdk/src/utils';
43+
import { ThreadListContextMenu } from '../views/context_menus/ThreadListContextMenu';
5144

5245
interface IProps {
5346
room: Room;
@@ -63,24 +56,8 @@ interface IState {
6356
thread?: Thread;
6457
editState?: EditorStateTransfer;
6558
replyToEvent?: MatrixEvent;
66-
threadOptionsPosition: DOMRect | null;
67-
copyingPhase: CopyingPhase;
6859
}
6960

70-
enum CopyingPhase {
71-
Idle,
72-
Copying,
73-
Failed,
74-
}
75-
76-
const contextMenuBelow = (elementRect: DOMRect) => {
77-
// align the context menu's icons with the icon which opened the context menu
78-
const left = elementRect.left + window.pageXOffset + elementRect.width;
79-
const top = elementRect.bottom + window.pageYOffset + 17;
80-
const chevronFace = ChevronFace.None;
81-
return { left, top, chevronFace };
82-
};
83-
8461
@replaceableComponent("structures.ThreadView")
8562
export default class ThreadView extends React.Component<IProps, IState> {
8663
static contextType = RoomContext;
@@ -90,12 +67,8 @@ export default class ThreadView extends React.Component<IProps, IState> {
9067

9168
constructor(props: IProps) {
9269
super(props);
93-
this.state = {
94-
threadOptionsPosition: null,
95-
copyingPhase: CopyingPhase.Idle,
96-
};
70+
this.state = {};
9771
}
98-
9972
public componentDidMount(): void {
10073
this.setupThread(this.props.mxEvent);
10174
this.dispatcherRef = dis.register(this.onAction);
@@ -210,95 +183,12 @@ export default class ThreadView extends React.Component<IProps, IState> {
210183
}
211184
};
212185

213-
private onThreadOptionsClick = (ev: ButtonEvent): void => {
214-
if (this.isThreadOptionsVisible) {
215-
this.closeThreadOptions();
216-
} else {
217-
const position = ev.currentTarget.getBoundingClientRect();
218-
this.setState({
219-
threadOptionsPosition: position,
220-
});
221-
}
222-
};
223-
224-
private closeThreadOptions = (): void => {
225-
this.setState({
226-
threadOptionsPosition: null,
227-
});
228-
};
229-
230-
private get isThreadOptionsVisible(): boolean {
231-
return !!this.state.threadOptionsPosition;
232-
}
233-
234-
private viewInRoom = (evt: ButtonEvent): void => {
235-
evt.preventDefault();
236-
evt.stopPropagation();
237-
dis.dispatch({
238-
action: 'view_room',
239-
event_id: this.props.mxEvent.getId(),
240-
highlighted: true,
241-
room_id: this.props.mxEvent.getRoomId(),
242-
});
243-
this.closeThreadOptions();
244-
};
245-
246-
private copyLinkToThread = async (evt: ButtonEvent): Promise<void> => {
247-
evt.preventDefault();
248-
evt.stopPropagation();
249-
250-
const matrixToUrl = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
251-
252-
this.setState({
253-
copyingPhase: CopyingPhase.Copying,
254-
});
255-
256-
const hasSuccessfullyCopied = await copyPlaintext(matrixToUrl);
257-
258-
if (hasSuccessfullyCopied) {
259-
await sleep(500);
260-
} else {
261-
this.setState({ copyingPhase: CopyingPhase.Failed });
262-
await sleep(2500);
263-
}
264-
265-
this.setState({ copyingPhase: CopyingPhase.Idle });
266-
267-
if (hasSuccessfullyCopied) {
268-
this.closeThreadOptions();
269-
}
270-
};
271-
272186
private renderThreadViewHeader = (): JSX.Element => {
273187
return <div className="mx_ThreadPanel__header">
274188
<span>{ _t("Thread") }</span>
275-
<ContextMenuTooltipButton
276-
className="mx_ThreadPanel_button mx_ThreadPanel_OptionsButton"
277-
onClick={this.onThreadOptionsClick}
278-
title={_t("Thread options")}
279-
isExpanded={this.isThreadOptionsVisible}
280-
/>
281-
{ this.isThreadOptionsVisible && (<IconizedContextMenu
282-
onFinished={this.closeThreadOptions}
283-
className="mx_RoomTile_contextMenu"
284-
compact
285-
rightAligned
286-
{...contextMenuBelow(this.state.threadOptionsPosition)}
287-
>
288-
<IconizedContextMenuOptionList>
289-
<IconizedContextMenuOption
290-
onClick={(e) => this.viewInRoom(e)}
291-
label={_t("View in room")}
292-
iconClassName="mx_ThreadPanel_viewInRoom"
293-
/>
294-
<IconizedContextMenuOption
295-
onClick={(e) => this.copyLinkToThread(e)}
296-
label={_t("Copy link to thread")}
297-
iconClassName="mx_ThreadPanel_copyLinkToThread"
298-
/>
299-
</IconizedContextMenuOptionList>
300-
</IconizedContextMenu>) }
301-
189+
<ThreadListContextMenu
190+
mxEvent={this.props.mxEvent}
191+
permalinkCreator={this.props.permalinkCreator} />
302192
</div>;
303193
};
304194

0 commit comments

Comments
 (0)