Fix space hierarchy not updating when user mutates it

This commit is contained in:
Michael Telatynski 2021-07-28 19:08:59 +01:00
parent 5d9e421de0
commit ed950875e7
5 changed files with 57 additions and 42 deletions

View file

@ -44,11 +44,13 @@ import { getChildOrder } from "../../stores/SpaceStore";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { linkifyElement } from "../../HtmlUtils"; import { linkifyElement } from "../../HtmlUtils";
import { getDisplayAliasForAliasSet } from "../../Rooms"; import { getDisplayAliasForAliasSet } from "../../Rooms";
import { useDispatcher } from "../../hooks/useDispatcher";
import defaultDispatcher from "../../dispatcher/dispatcher";
import { Action } from "../../dispatcher/actions";
interface IHierarchyProps { interface IHierarchyProps {
space: Room; space: Room;
initialText?: string; initialText?: string;
refreshToken?: any;
additionalButtons?: ReactNode; additionalButtons?: ReactNode;
showRoom(room: ISpaceSummaryRoom, viaServers?: string[], autoJoin?: boolean): void; showRoom(room: ISpaceSummaryRoom, viaServers?: string[], autoJoin?: boolean): void;
} }
@ -315,18 +317,25 @@ export const HierarchyLevel = ({
</React.Fragment>; </React.Fragment>;
}; };
// mutate argument refreshToken to force a reload export const useSpaceSummary = (space: Room): [
export const useSpaceSummary = (cli: MatrixClient, space: Room, refreshToken?: any): [
null, null,
ISpaceSummaryRoom[], ISpaceSummaryRoom[],
Map<string, Map<string, ISpaceSummaryEvent>>?, Map<string, Map<string, ISpaceSummaryEvent>>?,
Map<string, Set<string>>?, Map<string, Set<string>>?,
Map<string, Set<string>>?, Map<string, Set<string>>?,
] | [Error] => { ] | [Error] => {
// crude temporary refresh token approach until we have pagination and rework the data flow here
const [refreshToken, setRefreshToken] = useState(0);
useDispatcher(defaultDispatcher, (payload => {
if (payload.action === Action.UpdateSpaceHierarchy) {
setRefreshToken(t => t + 1);
}
}));
// TODO pagination // TODO pagination
return useAsyncMemo(async () => { return useAsyncMemo(async () => {
try { try {
const data = await cli.getSpaceSummary(space.roomId); const data = await space.client.getSpaceSummary(space.roomId);
const parentChildRelations = new EnhancedMap<string, Map<string, ISpaceSummaryEvent>>(); const parentChildRelations = new EnhancedMap<string, Map<string, ISpaceSummaryEvent>>();
const childParentRelations = new EnhancedMap<string, Set<string>>(); const childParentRelations = new EnhancedMap<string, Set<string>>();
@ -354,7 +363,6 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
space, space,
initialText = "", initialText = "",
showRoom, showRoom,
refreshToken,
additionalButtons, additionalButtons,
children, children,
}) => { }) => {
@ -364,7 +372,7 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
const [selected, setSelected] = useState(new Map<string, Set<string>>()); // Map<parentId, Set<childId>> const [selected, setSelected] = useState(new Map<string, Set<string>>()); // Map<parentId, Set<childId>>
const [summaryError, rooms, parentChildMap, viaMap, childParentMap] = useSpaceSummary(cli, space, refreshToken); const [summaryError, rooms, parentChildMap, viaMap, childParentMap] = useSpaceSummary(space);
const roomsMap = useMemo(() => { const roomsMap = useMemo(() => {
if (!rooms) return null; if (!rooms) return null;

View file

@ -56,7 +56,6 @@ import {
} from "../../utils/space"; } from "../../utils/space";
import { showRoom, SpaceHierarchy } from "./SpaceRoomDirectory"; import { showRoom, SpaceHierarchy } from "./SpaceRoomDirectory";
import MemberAvatar from "../views/avatars/MemberAvatar"; import MemberAvatar from "../views/avatars/MemberAvatar";
import { useStateToggle } from "../../hooks/useStateToggle";
import SpaceStore from "../../stores/SpaceStore"; import SpaceStore from "../../stores/SpaceStore";
import FacePile from "../views/elements/FacePile"; import FacePile from "../views/elements/FacePile";
import { import {
@ -318,7 +317,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
</div>; </div>;
}; };
const SpaceLandingAddButton = ({ space, onNewRoomAdded }) => { const SpaceLandingAddButton = ({ space }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
let contextMenu; let contextMenu;
@ -342,36 +341,28 @@ const SpaceLandingAddButton = ({ space, onNewRoomAdded }) => {
closeMenu(); closeMenu();
if (await showCreateNewRoom(space)) { if (await showCreateNewRoom(space)) {
onNewRoomAdded(); defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
} }
}} }}
/> />
<IconizedContextMenuOption <IconizedContextMenuOption
label={_t("Add existing room")} label={_t("Add existing room")}
iconClassName="mx_RoomList_iconHash" iconClassName="mx_RoomList_iconHash"
onClick={async (e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
closeMenu(); closeMenu();
showAddExistingRooms(space);
const [added] = await showAddExistingRooms(space);
if (added) {
onNewRoomAdded();
}
}} }}
/> />
<IconizedContextMenuOption <IconizedContextMenuOption
label={_t("Add subspace")} label={_t("Add subspace")}
iconClassName="mx_RoomList_iconPlus" iconClassName="mx_RoomList_iconPlus"
onClick={async (e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
closeMenu(); closeMenu();
showCreateNewSubspace(space);
const [added] = await showCreateNewSubspace(space);
if (added) {
onNewRoomAdded();
}
}} }}
> >
<BetaPill /> <BetaPill />
@ -416,11 +407,9 @@ const SpaceLanding = ({ space }) => {
const canAddRooms = myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId); const canAddRooms = myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
const [refreshToken, forceUpdate] = useStateToggle(false);
let addRoomButton; let addRoomButton;
if (canAddRooms) { if (canAddRooms) {
addRoomButton = <SpaceLandingAddButton space={space} onNewRoomAdded={forceUpdate} />; addRoomButton = <SpaceLandingAddButton space={space} />;
} }
let settingsButton; let settingsButton;
@ -470,12 +459,7 @@ const SpaceLanding = ({ space }) => {
<SpaceFeedbackPrompt /> <SpaceFeedbackPrompt />
<hr /> <hr />
<SpaceHierarchy <SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
space={space}
showRoom={showRoom}
refreshToken={refreshToken}
additionalButtons={addRoomButton}
/>
</div>; </div>;
}; };

View file

@ -249,7 +249,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
let noResults = true; let noResults = true;
if ((roomsRenderer && rooms.length > 0) || if ((roomsRenderer && rooms.length > 0) ||
(dmsRenderer && dms.length > 0) || (dmsRenderer && dms.length > 0) ||
(!roomsRenderer && !dmsRenderer && spacesRenderer && dms.length > 0) // only count spaces when alone (!roomsRenderer && !dmsRenderer && spacesRenderer && spaces.length > 0) // only count spaces when alone
) { ) {
noResults = false; noResults = false;
} }

View file

@ -193,4 +193,9 @@ export enum Action {
* Switches space. Should be used with SwitchSpacePayload. * Switches space. Should be used with SwitchSpacePayload.
*/ */
SwitchSpace = "switch_space", SwitchSpace = "switch_space",
/**
* Signals to the visible space hierarchy that a change has occurred an that it should refresh.
*/
UpdateSpaceHierarchy = "update_space_hierarchy",
} }

View file

@ -30,6 +30,9 @@ import InfoDialog from "../components/views/dialogs/InfoDialog";
import { showRoomInviteDialog } from "../RoomInvite"; import { showRoomInviteDialog } from "../RoomInvite";
import CreateSubspaceDialog from "../components/views/dialogs/CreateSubspaceDialog"; import CreateSubspaceDialog from "../components/views/dialogs/CreateSubspaceDialog";
import AddExistingSubspaceDialog from "../components/views/dialogs/AddExistingSubspaceDialog"; import AddExistingSubspaceDialog from "../components/views/dialogs/AddExistingSubspaceDialog";
import defaultDispatcher from "../dispatcher/dispatcher";
import RoomViewStore from "../stores/RoomViewStore";
import { Action } from "../dispatcher/actions";
export const shouldShowSpaceSettings = (space: Room) => { export const shouldShowSpaceSettings = (space: Room) => {
const userId = space.client.getUserId(); const userId = space.client.getUserId();
@ -56,8 +59,8 @@ export const showSpaceSettings = (space: Room) => {
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
}; };
export const showAddExistingRooms = async (space: Room): Promise<[boolean]> => { export const showAddExistingRooms = (space: Room): void => {
return Modal.createTrackedDialog( Modal.createTrackedDialog(
"Space Landing", "Space Landing",
"Add Existing", "Add Existing",
AddExistingToSpaceDialog, AddExistingToSpaceDialog,
@ -65,12 +68,17 @@ export const showAddExistingRooms = async (space: Room): Promise<[boolean]> => {
onCreateRoomClick: () => showCreateNewRoom(space), onCreateRoomClick: () => showCreateNewRoom(space),
onAddSubspaceClick: () => showAddExistingSubspace(space), onAddSubspaceClick: () => showAddExistingSubspace(space),
space, space,
onFinished: (added: boolean) => {
if (added && RoomViewStore.getRoomId() === space.roomId) {
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
}
},
}, },
"mx_AddExistingToSpaceDialog_wrapper", "mx_AddExistingToSpaceDialog_wrapper",
).finished as Promise<[boolean]>; );
}; };
export const showCreateNewRoom = async (space: Room) => { export const showCreateNewRoom = async (space: Room): Promise<boolean> => {
const modal = Modal.createTrackedDialog<[boolean, IOpts]>( const modal = Modal.createTrackedDialog<[boolean, IOpts]>(
"Space Landing", "Space Landing",
"Create Room", "Create Room",
@ -87,7 +95,7 @@ export const showCreateNewRoom = async (space: Room) => {
return shouldCreate; return shouldCreate;
}; };
export const showSpaceInvite = (space: Room, initialText = "") => { export const showSpaceInvite = (space: Room, initialText = ""): void => {
if (space.getJoinRule() === "public") { if (space.getJoinRule() === "public") {
const modal = Modal.createTrackedDialog("Space Invite", "User Menu", InfoDialog, { const modal = Modal.createTrackedDialog("Space Invite", "User Menu", InfoDialog, {
title: _t("Invite to %(spaceName)s", { spaceName: space.name }), title: _t("Invite to %(spaceName)s", { spaceName: space.name }),
@ -105,28 +113,38 @@ export const showSpaceInvite = (space: Room, initialText = "") => {
} }
}; };
export const showAddExistingSubspace = async (space: Room): Promise<[boolean]> => { export const showAddExistingSubspace = (space: Room): void => {
return Modal.createTrackedDialog( Modal.createTrackedDialog(
"Space Landing", "Space Landing",
"Create Subspace", "Create Subspace",
AddExistingSubspaceDialog, AddExistingSubspaceDialog,
{ {
space, space,
onCreateSubspaceClick: () => showCreateNewSubspace(space), onCreateSubspaceClick: () => showCreateNewSubspace(space),
onFinished: (added: boolean) => {
if (added && RoomViewStore.getRoomId() === space.roomId) {
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
}
},
}, },
"mx_AddExistingToSpaceDialog_wrapper", "mx_AddExistingToSpaceDialog_wrapper",
).finished as Promise<[boolean]>; );
}; };
export const showCreateNewSubspace = async (space: Room): Promise<[boolean]> => { export const showCreateNewSubspace = (space: Room): void => {
return Modal.createTrackedDialog( Modal.createTrackedDialog(
"Space Landing", "Space Landing",
"Create Subspace", "Create Subspace",
CreateSubspaceDialog, CreateSubspaceDialog,
{ {
space, space,
onAddExistingSpaceClick: () => showAddExistingSubspace(space), onAddExistingSpaceClick: () => showAddExistingSubspace(space),
onFinished: (added: boolean) => {
if (added && RoomViewStore.getRoomId() === space.roomId) {
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
}
},
}, },
"mx_CreateSubspaceDialog_wrapper", "mx_CreateSubspaceDialog_wrapper",
).finished as Promise<[boolean]>; );
}; };