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

Commit 6338ced

Browse files
authored
Merge pull request #5977 from matrix-org/t3chguy/fix/17176
Iterate Spaces admin UX around room management
2 parents 6f28964 + 237399f commit 6338ced

File tree

7 files changed

+213
-173
lines changed

7 files changed

+213
-173
lines changed

res/css/structures/_SpaceRoomDirectory.scss

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,19 @@ limitations under the License.
8686
color: $primary-fg-color;
8787

8888
.mx_AccessibleButton {
89-
padding: 2px 8px;
89+
padding: 4px 12px;
9090
font-weight: normal;
9191

9292
& + .mx_AccessibleButton {
9393
margin-left: 16px;
9494
}
9595
}
9696

97+
.mx_AccessibleButton_kind_danger_outline,
98+
.mx_AccessibleButton_kind_primary_outline {
99+
padding: 3px 12px; // to account for the 1px border
100+
}
101+
97102
> span {
98103
margin-left: auto;
99104
}
@@ -246,11 +251,17 @@ limitations under the License.
246251
grid-row: 1/3;
247252

248253
.mx_AccessibleButton {
249-
padding: 8px 18px;
254+
line-height: $font-24px;
255+
padding: 4px 16px;
250256
display: inline-block;
251257
visibility: hidden;
252258
}
253259

260+
.mx_AccessibleButton_kind_danger_outline,
261+
.mx_AccessibleButton_kind_primary_outline {
262+
padding: 3px 16px; // to account for the 1px border
263+
}
264+
254265
.mx_Checkbox {
255266
display: inline-flex;
256267
vertical-align: middle;

res/css/structures/_SpaceRoomView.scss

Lines changed: 23 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ $SpaceRoomViewInnerWidth: 428px;
238238

239239
.mx_SpaceRoomView_landing_inviteButton {
240240
position: relative;
241-
padding-left: 40px;
241+
padding: 4px 18px 4px 40px;
242+
line-height: $font-24px;
242243
height: min-content;
243244

244245
&::before {
@@ -254,6 +255,27 @@ $SpaceRoomViewInnerWidth: 428px;
254255
mask-image: url('$(res)/img/element-icons/room/invite.svg');
255256
}
256257
}
258+
259+
.mx_SpaceRoomView_landing_settingsButton {
260+
position: relative;
261+
margin-left: 16px;
262+
width: 24px;
263+
height: 24px;
264+
265+
&::before {
266+
position: absolute;
267+
content: "";
268+
left: 0;
269+
top: 0;
270+
height: 24px;
271+
width: 24px;
272+
background: $tertiary-fg-color;
273+
mask-position: center;
274+
mask-size: contain;
275+
mask-repeat: no-repeat;
276+
mask-image: url('$(res)/img/element-icons/settings.svg');
277+
}
278+
}
257279
}
258280

259281
.mx_SpaceRoomView_landing_topic {
@@ -268,80 +290,6 @@ $SpaceRoomViewInnerWidth: 428px;
268290
background-color: $groupFilterPanel-bg-color;
269291
}
270292

271-
.mx_SpaceRoomView_landing_adminButtons {
272-
margin-top: 24px;
273-
274-
.mx_AccessibleButton {
275-
position: relative;
276-
width: 160px;
277-
height: 124px;
278-
box-sizing: border-box;
279-
padding: 72px 16px 0;
280-
border-radius: 12px;
281-
border: 1px solid $input-border-color;
282-
margin-right: 28px;
283-
margin-bottom: 20px;
284-
font-size: $font-14px;
285-
display: inline-block;
286-
vertical-align: bottom;
287-
288-
&:last-child {
289-
margin-right: 0;
290-
}
291-
292-
&:hover {
293-
background-color: rgba(141, 151, 165, 0.1);
294-
}
295-
296-
&::before, &::after {
297-
position: absolute;
298-
content: "";
299-
left: 16px;
300-
top: 16px;
301-
height: 40px;
302-
width: 40px;
303-
border-radius: 20px;
304-
}
305-
306-
&::after {
307-
mask-position: center;
308-
mask-size: 30px;
309-
mask-repeat: no-repeat;
310-
background: #ffffff; // white icon fill
311-
}
312-
313-
&.mx_SpaceRoomView_landing_addButton {
314-
&::before {
315-
background-color: #ac3ba8;
316-
}
317-
318-
&::after {
319-
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
320-
}
321-
}
322-
323-
&.mx_SpaceRoomView_landing_createButton {
324-
&::before {
325-
background-color: #368bd6;
326-
}
327-
328-
&::after {
329-
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
330-
}
331-
}
332-
333-
&.mx_SpaceRoomView_landing_settingsButton {
334-
&::before {
335-
background-color: #5c56f5;
336-
}
337-
338-
&::after {
339-
mask-image: url('$(res)/img/element-icons/settings.svg');
340-
}
341-
}
342-
}
343-
}
344-
345293
.mx_SearchBox {
346294
margin: 0 0 20px;
347295
}

res/css/views/elements/_AccessibleButton.scss

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,16 @@ limitations under the License.
7676
border: 1px solid $button-danger-bg-color;
7777
}
7878

79-
.mx_AccessibleButton_kind_danger.mx_AccessibleButton_disabled,
80-
.mx_AccessibleButton_kind_danger_outline.mx_AccessibleButton_disabled {
79+
.mx_AccessibleButton_kind_danger.mx_AccessibleButton_disabled {
8180
color: $button-danger-disabled-fg-color;
8281
background-color: $button-danger-disabled-bg-color;
8382
}
8483

84+
.mx_AccessibleButton_kind_danger_outline.mx_AccessibleButton_disabled {
85+
color: $button-danger-disabled-bg-color;
86+
border-color: $button-danger-disabled-bg-color;
87+
}
88+
8589
.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_danger_sm {
8690
padding: 5px 12px;
8791
color: $button-danger-fg-color;

src/components/structures/SpaceRoomDirectory.tsx

Lines changed: 86 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, {useMemo, useState} from "react";
17+
import React, {ReactNode, useMemo, useState} from "react";
1818
import {Room} from "matrix-js-sdk/src/models/room";
1919
import {MatrixClient} from "matrix-js-sdk/src/client";
2020
import {EventType, RoomType} from "matrix-js-sdk/src/@types/event";
@@ -24,7 +24,7 @@ import {sortBy} from "lodash";
2424
import {MatrixClientPeg} from "../../MatrixClientPeg";
2525
import dis from "../../dispatcher/dispatcher";
2626
import {_t} from "../../languageHandler";
27-
import AccessibleButton from "../views/elements/AccessibleButton";
27+
import AccessibleButton, {ButtonEvent} from "../views/elements/AccessibleButton";
2828
import BaseDialog from "../views/dialogs/BaseDialog";
2929
import Spinner from "../views/elements/Spinner";
3030
import SearchBox from "./SearchBox";
@@ -40,11 +40,13 @@ import InfoTooltip from "../views/elements/InfoTooltip";
4040
import TextWithTooltip from "../views/elements/TextWithTooltip";
4141
import {useStateToggle} from "../../hooks/useStateToggle";
4242
import {getOrder} from "../../stores/SpaceStore";
43+
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
4344

4445
interface IHierarchyProps {
4546
space: Room;
4647
initialText?: string;
4748
refreshToken?: any;
49+
additionalButtons?: ReactNode;
4850
showRoom(room: ISpaceSummaryRoom, viaServers?: string[], autoJoin?: boolean): void;
4951
}
5052

@@ -107,8 +109,16 @@ const Tile: React.FC<ITileProps> = ({
107109
const cliRoom = cli.getRoom(room.room_id);
108110
const myMembership = cliRoom?.getMyMembership();
109111

110-
const onPreviewClick = () => onViewRoomClick(false);
111-
const onJoinClick = () => onViewRoomClick(true);
112+
const onPreviewClick = (ev: ButtonEvent) => {
113+
ev.preventDefault();
114+
ev.stopPropagation();
115+
onViewRoomClick(false);
116+
}
117+
const onJoinClick = (ev: ButtonEvent) => {
118+
ev.preventDefault();
119+
ev.stopPropagation();
120+
onViewRoomClick(true);
121+
}
112122

113123
let button;
114124
if (myMembership === "join") {
@@ -355,6 +365,7 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
355365
initialText = "",
356366
showRoom,
357367
refreshToken,
368+
additionalButtons,
358369
children,
359370
}) => {
360371
const cli = MatrixClientPeg.get();
@@ -420,78 +431,83 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
420431
countsStr = _t("%(count)s rooms", { count: numRooms, numSpaces });
421432
}
422433

423-
let editSection;
434+
let manageButtons;
424435
if (space.getMyMembership() === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId)) {
425436
const selectedRelations = Array.from(selected.keys()).flatMap(parentId => {
426437
return [...selected.get(parentId).values()].map(childId => [parentId, childId]) as [string, string][];
427438
});
428439

429-
let buttons;
430-
if (selectedRelations.length) {
431-
const selectionAllSuggested = selectedRelations.every(([parentId, childId]) => {
432-
return parentChildMap.get(parentId)?.get(childId)?.content.suggested;
433-
});
440+
const selectionAllSuggested = selectedRelations.every(([parentId, childId]) => {
441+
return parentChildMap.get(parentId)?.get(childId)?.content.suggested;
442+
});
434443

435-
const disabled = removing || saving;
444+
const disabled = !selectedRelations.length || removing || saving;
436445

437-
buttons = <>
438-
<AccessibleButton
439-
onClick={async () => {
440-
setRemoving(true);
441-
try {
442-
for (const [parentId, childId] of selectedRelations) {
443-
await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, childId);
444-
parentChildMap.get(parentId).get(childId).content = {};
445-
parentChildMap.set(parentId, new Map(parentChildMap.get(parentId)));
446-
}
447-
} catch (e) {
448-
setError(_t("Failed to remove some rooms. Try again later"));
446+
let Button: React.ComponentType<React.ComponentProps<typeof AccessibleButton>> = AccessibleButton;
447+
let props = {};
448+
if (!selectedRelations.length) {
449+
Button = AccessibleTooltipButton;
450+
props = {
451+
tooltip: _t("Select a room below first"),
452+
yOffset: -40,
453+
};
454+
}
455+
456+
manageButtons = <>
457+
<Button
458+
{...props}
459+
onClick={async () => {
460+
setRemoving(true);
461+
try {
462+
for (const [parentId, childId] of selectedRelations) {
463+
await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, childId);
464+
parentChildMap.get(parentId).get(childId).content = {};
465+
parentChildMap.set(parentId, new Map(parentChildMap.get(parentId)));
449466
}
450-
setRemoving(false);
451-
}}
452-
kind="danger_outline"
453-
disabled={disabled}
454-
>
455-
{ removing ? _t("Removing...") : _t("Remove") }
456-
</AccessibleButton>
457-
<AccessibleButton
458-
onClick={async () => {
459-
setSaving(true);
460-
try {
461-
for (const [parentId, childId] of selectedRelations) {
462-
const suggested = !selectionAllSuggested;
463-
const existingContent = parentChildMap.get(parentId)?.get(childId)?.content;
464-
if (!existingContent || existingContent.suggested === suggested) continue;
465-
466-
const content = {
467-
...existingContent,
468-
suggested: !selectionAllSuggested,
469-
};
470-
471-
await cli.sendStateEvent(parentId, EventType.SpaceChild, content, childId);
472-
473-
parentChildMap.get(parentId).get(childId).content = content;
474-
parentChildMap.set(parentId, new Map(parentChildMap.get(parentId)));
475-
}
476-
} catch (e) {
477-
setError("Failed to update some suggestions. Try again later");
467+
} catch (e) {
468+
setError(_t("Failed to remove some rooms. Try again later"));
469+
}
470+
setRemoving(false);
471+
}}
472+
kind="danger_outline"
473+
disabled={disabled}
474+
>
475+
{ removing ? _t("Removing...") : _t("Remove") }
476+
</Button>
477+
<Button
478+
{...props}
479+
onClick={async () => {
480+
setSaving(true);
481+
try {
482+
for (const [parentId, childId] of selectedRelations) {
483+
const suggested = !selectionAllSuggested;
484+
const existingContent = parentChildMap.get(parentId)?.get(childId)?.content;
485+
if (!existingContent || existingContent.suggested === suggested) continue;
486+
487+
const content = {
488+
...existingContent,
489+
suggested: !selectionAllSuggested,
490+
};
491+
492+
await cli.sendStateEvent(parentId, EventType.SpaceChild, content, childId);
493+
494+
parentChildMap.get(parentId).get(childId).content = content;
495+
parentChildMap.set(parentId, new Map(parentChildMap.get(parentId)));
478496
}
479-
setSaving(false);
480-
}}
481-
kind="primary_outline"
482-
disabled={disabled}
483-
>
484-
{ saving
485-
? _t("Saving...")
486-
: (selectionAllSuggested ? _t("Mark as not suggested") : _t("Mark as suggested"))
497+
} catch (e) {
498+
setError("Failed to update some suggestions. Try again later");
487499
}
488-
</AccessibleButton>
489-
</>;
490-
}
491-
492-
editSection = <span>
493-
{ buttons }
494-
</span>;
500+
setSaving(false);
501+
}}
502+
kind="primary_outline"
503+
disabled={disabled}
504+
>
505+
{ saving
506+
? _t("Saving...")
507+
: (selectionAllSuggested ? _t("Mark as not suggested") : _t("Mark as suggested"))
508+
}
509+
</Button>
510+
</>;
495511
}
496512

497513
let results;
@@ -537,7 +553,10 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
537553
content = <>
538554
<div className="mx_SpaceRoomDirectory_listHeader">
539555
{ countsStr }
540-
{ editSection }
556+
<span>
557+
{ additionalButtons }
558+
{ manageButtons }
559+
</span>
541560
</div>
542561
{ error && <div className="mx_SpaceRoomDirectory_error">
543562
{ error }

0 commit comments

Comments
 (0)