You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	Improve subspaces and some utilities around room/space creation
This commit is contained in:
		@@ -76,6 +76,7 @@
 | 
				
			|||||||
@import "./views/dialogs/_CreateCommunityPrototypeDialog.scss";
 | 
					@import "./views/dialogs/_CreateCommunityPrototypeDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
 | 
					@import "./views/dialogs/_CreateGroupDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
 | 
					@import "./views/dialogs/_CreateRoomDialog.scss";
 | 
				
			||||||
 | 
					@import "./views/dialogs/_CreateSubspaceDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
 | 
					@import "./views/dialogs/_DeactivateAccountDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
 | 
					@import "./views/dialogs/_DevtoolsDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
 | 
					@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
 | 
				
			||||||
@@ -85,6 +86,7 @@
 | 
				
			|||||||
@import "./views/dialogs/_HostSignupDialog.scss";
 | 
					@import "./views/dialogs/_HostSignupDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_IncomingSasDialog.scss";
 | 
					@import "./views/dialogs/_IncomingSasDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_InviteDialog.scss";
 | 
					@import "./views/dialogs/_InviteDialog.scss";
 | 
				
			||||||
 | 
					@import "./views/dialogs/_JoinRuleDropdown.scss";
 | 
				
			||||||
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
 | 
					@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_ManageRestrictedJoinRuleDialog.scss";
 | 
					@import "./views/dialogs/_ManageRestrictedJoinRuleDialog.scss";
 | 
				
			||||||
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
 | 
					@import "./views/dialogs/_MessageEditHistoryDialog.scss";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,6 +99,10 @@ limitations under the License.
 | 
				
			|||||||
            .mx_IconizedContextMenu_icon + .mx_IconizedContextMenu_label {
 | 
					            .mx_IconizedContextMenu_icon + .mx_IconizedContextMenu_label {
 | 
				
			||||||
                padding-left: 14px;
 | 
					                padding-left: 14px;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .mx_BetaCard_betaPill {
 | 
				
			||||||
 | 
					                margin-left: 16px;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,11 +54,16 @@ limitations under the License.
 | 
				
			|||||||
            display: flex;
 | 
					            display: flex;
 | 
				
			||||||
            margin-top: 12px;
 | 
					            margin-top: 12px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling
 | 
					            .mx_DecoratedRoomAvatar, // we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling
 | 
				
			||||||
            .mx_DecoratedRoomAvatar {
 | 
					            .mx_BaseAvatar.mx_RoomAvatar_isSpaceRoom {
 | 
				
			||||||
                margin-right: 12px;
 | 
					                margin-right: 12px;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            img.mx_RoomAvatar_isSpaceRoom,
 | 
				
			||||||
 | 
					            .mx_RoomAvatar_isSpaceRoom img {
 | 
				
			||||||
 | 
					                border-radius: 8px;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            .mx_AddExistingToSpace_entry_name {
 | 
					            .mx_AddExistingToSpace_entry_name {
 | 
				
			||||||
                font-size: $font-15px;
 | 
					                font-size: $font-15px;
 | 
				
			||||||
                line-height: 30px;
 | 
					                line-height: 30px;
 | 
				
			||||||
@@ -73,41 +78,12 @@ limitations under the License.
 | 
				
			|||||||
                align-items: center;
 | 
					                align-items: center;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .mx_AddExistingToSpace_section_spaces {
 | 
					        .mx_AccessibleButton_kind_link {
 | 
				
			||||||
        .mx_BaseAvatar {
 | 
					            font-size: $font-12px;
 | 
				
			||||||
            margin-right: 12px;
 | 
					            line-height: $font-15px;
 | 
				
			||||||
        }
 | 
					            margin-top: 8px;
 | 
				
			||||||
 | 
					            padding: 0;
 | 
				
			||||||
        .mx_BaseAvatar_image {
 | 
					 | 
				
			||||||
            border-radius: 8px;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .mx_AddExistingToSpace_section_experimental {
 | 
					 | 
				
			||||||
        position: relative;
 | 
					 | 
				
			||||||
        border-radius: 8px;
 | 
					 | 
				
			||||||
        margin: 12px 0;
 | 
					 | 
				
			||||||
        padding: 8px 8px 8px 42px;
 | 
					 | 
				
			||||||
        background-color: $header-panel-bg-color;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        font-size: $font-12px;
 | 
					 | 
				
			||||||
        line-height: $font-15px;
 | 
					 | 
				
			||||||
        color: $secondary-fg-color;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        &::before {
 | 
					 | 
				
			||||||
            content: '';
 | 
					 | 
				
			||||||
            position: absolute;
 | 
					 | 
				
			||||||
            left: 10px;
 | 
					 | 
				
			||||||
            top: calc(50% - 8px); // vertical centering
 | 
					 | 
				
			||||||
            height: 16px;
 | 
					 | 
				
			||||||
            width: 16px;
 | 
					 | 
				
			||||||
            background-color: $secondary-fg-color;
 | 
					 | 
				
			||||||
            mask-repeat: no-repeat;
 | 
					 | 
				
			||||||
            mask-size: contain;
 | 
					 | 
				
			||||||
            mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
 | 
					 | 
				
			||||||
            mask-position: center;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -205,77 +181,77 @@ limitations under the License.
 | 
				
			|||||||
    min-height: 0;
 | 
					    min-height: 0;
 | 
				
			||||||
    height: 80vh;
 | 
					    height: 80vh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .mx_Dialog_title {
 | 
					 | 
				
			||||||
        display: flex;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_BaseAvatar_image {
 | 
					 | 
				
			||||||
            border-radius: 8px;
 | 
					 | 
				
			||||||
            margin: 0;
 | 
					 | 
				
			||||||
            vertical-align: unset;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_BaseAvatar {
 | 
					 | 
				
			||||||
            display: inline-flex;
 | 
					 | 
				
			||||||
            margin: auto 16px auto 5px;
 | 
					 | 
				
			||||||
            vertical-align: middle;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        > div {
 | 
					 | 
				
			||||||
            > h1 {
 | 
					 | 
				
			||||||
                font-weight: $font-semi-bold;
 | 
					 | 
				
			||||||
                font-size: $font-18px;
 | 
					 | 
				
			||||||
                line-height: $font-22px;
 | 
					 | 
				
			||||||
                margin: 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .mx_AddExistingToSpaceDialog_onlySpace {
 | 
					 | 
				
			||||||
                color: $secondary-fg-color;
 | 
					 | 
				
			||||||
                font-size: $font-15px;
 | 
					 | 
				
			||||||
                line-height: $font-24px;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_Dropdown_input {
 | 
					 | 
				
			||||||
            border: none;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            > .mx_Dropdown_option {
 | 
					 | 
				
			||||||
                padding-left: 0;
 | 
					 | 
				
			||||||
                flex: unset;
 | 
					 | 
				
			||||||
                height: unset;
 | 
					 | 
				
			||||||
                color: $secondary-fg-color;
 | 
					 | 
				
			||||||
                font-size: $font-15px;
 | 
					 | 
				
			||||||
                line-height: $font-24px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                .mx_BaseAvatar {
 | 
					 | 
				
			||||||
                    display: none;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            .mx_Dropdown_menu {
 | 
					 | 
				
			||||||
                .mx_AddExistingToSpaceDialog_dropdownOptionActive {
 | 
					 | 
				
			||||||
                    color: $accent-color;
 | 
					 | 
				
			||||||
                    padding-right: 32px;
 | 
					 | 
				
			||||||
                    position: relative;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    &::before {
 | 
					 | 
				
			||||||
                        content: '';
 | 
					 | 
				
			||||||
                        width: 20px;
 | 
					 | 
				
			||||||
                        height: 20px;
 | 
					 | 
				
			||||||
                        top: 8px;
 | 
					 | 
				
			||||||
                        right: 0;
 | 
					 | 
				
			||||||
                        position: absolute;
 | 
					 | 
				
			||||||
                        mask-position: center;
 | 
					 | 
				
			||||||
                        mask-size: contain;
 | 
					 | 
				
			||||||
                        mask-repeat: no-repeat;
 | 
					 | 
				
			||||||
                        background-color: $accent-color;
 | 
					 | 
				
			||||||
                        mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .mx_AddExistingToSpace {
 | 
					    .mx_AddExistingToSpace {
 | 
				
			||||||
        display: contents;
 | 
					        display: contents;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mx_SubspaceSelector {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_BaseAvatar_image {
 | 
				
			||||||
 | 
					        border-radius: 8px;
 | 
				
			||||||
 | 
					        margin: 0;
 | 
				
			||||||
 | 
					        vertical-align: unset;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_BaseAvatar {
 | 
				
			||||||
 | 
					        display: inline-flex;
 | 
				
			||||||
 | 
					        margin: auto 16px auto 5px;
 | 
				
			||||||
 | 
					        vertical-align: middle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    > div {
 | 
				
			||||||
 | 
					        > h1 {
 | 
				
			||||||
 | 
					            font-weight: $font-semi-bold;
 | 
				
			||||||
 | 
					            font-size: $font-18px;
 | 
				
			||||||
 | 
					            line-height: $font-22px;
 | 
				
			||||||
 | 
					            margin: 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_Dropdown_input {
 | 
				
			||||||
 | 
					        border: none;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        > .mx_Dropdown_option {
 | 
				
			||||||
 | 
					            padding-left: 0;
 | 
				
			||||||
 | 
					            flex: unset;
 | 
				
			||||||
 | 
					            height: unset;
 | 
				
			||||||
 | 
					            color: $secondary-fg-color;
 | 
				
			||||||
 | 
					            font-size: $font-15px;
 | 
				
			||||||
 | 
					            line-height: $font-24px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .mx_BaseAvatar {
 | 
				
			||||||
 | 
					                display: none;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .mx_Dropdown_menu {
 | 
				
			||||||
 | 
					            .mx_SubspaceSelector_dropdownOptionActive {
 | 
				
			||||||
 | 
					                color: $accent-color;
 | 
				
			||||||
 | 
					                padding-right: 32px;
 | 
				
			||||||
 | 
					                position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                &::before {
 | 
				
			||||||
 | 
					                    content: '';
 | 
				
			||||||
 | 
					                    width: 20px;
 | 
				
			||||||
 | 
					                    height: 20px;
 | 
				
			||||||
 | 
					                    top: 8px;
 | 
				
			||||||
 | 
					                    right: 0;
 | 
				
			||||||
 | 
					                    position: absolute;
 | 
				
			||||||
 | 
					                    mask-position: center;
 | 
				
			||||||
 | 
					                    mask-size: contain;
 | 
				
			||||||
 | 
					                    mask-repeat: no-repeat;
 | 
				
			||||||
 | 
					                    background-color: $accent-color;
 | 
				
			||||||
 | 
					                    mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_SubspaceSelector_onlySpace {
 | 
				
			||||||
 | 
					        color: $secondary-fg-color;
 | 
				
			||||||
 | 
					        font-size: $font-15px;
 | 
				
			||||||
 | 
					        line-height: $font-24px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,56 +109,4 @@ limitations under the License.
 | 
				
			|||||||
        margin: 0 85px 0 0;
 | 
					        margin: 0 85px 0 0;
 | 
				
			||||||
        font-size: $font-12px;
 | 
					        font-size: $font-12px;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    .mx_Dropdown {
 | 
					 | 
				
			||||||
        margin-bottom: 8px;
 | 
					 | 
				
			||||||
        font-weight: normal;
 | 
					 | 
				
			||||||
        font-family: $font-family;
 | 
					 | 
				
			||||||
        font-size: $font-14px;
 | 
					 | 
				
			||||||
        color: $primary-fg-color;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_Dropdown_input {
 | 
					 | 
				
			||||||
            border: 1px solid $input-border-color;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_Dropdown_option {
 | 
					 | 
				
			||||||
            font-size: $font-14px;
 | 
					 | 
				
			||||||
            line-height: $font-32px;
 | 
					 | 
				
			||||||
            height: 32px;
 | 
					 | 
				
			||||||
            min-height: 32px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            > div {
 | 
					 | 
				
			||||||
                padding-left: 30px;
 | 
					 | 
				
			||||||
                position: relative;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                &::before {
 | 
					 | 
				
			||||||
                    content: "";
 | 
					 | 
				
			||||||
                    position: absolute;
 | 
					 | 
				
			||||||
                    height: 16px;
 | 
					 | 
				
			||||||
                    width: 16px;
 | 
					 | 
				
			||||||
                    left: 6px;
 | 
					 | 
				
			||||||
                    top: 8px;
 | 
					 | 
				
			||||||
                    mask-repeat: no-repeat;
 | 
					 | 
				
			||||||
                    mask-position: center;
 | 
					 | 
				
			||||||
                    background-color: $secondary-fg-color;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_CreateRoomDialog_dropdown_invite::before {
 | 
					 | 
				
			||||||
            mask-image: url('$(res)/img/element-icons/lock.svg');
 | 
					 | 
				
			||||||
            mask-size: contain;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_CreateRoomDialog_dropdown_public::before {
 | 
					 | 
				
			||||||
            mask-image: url('$(res)/img/globe.svg');
 | 
					 | 
				
			||||||
            mask-size: 12px;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .mx_CreateRoomDialog_dropdown_restricted::before {
 | 
					 | 
				
			||||||
            mask-image: url('$(res)/img/element-icons/community-members.svg');
 | 
					 | 
				
			||||||
            mask-size: contain;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										75
									
								
								res/css/views/dialogs/_CreateSubspaceDialog.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								res/css/views/dialogs/_CreateSubspaceDialog.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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_CreateSubspaceDialog_wrapper {
 | 
				
			||||||
 | 
					    .mx_Dialog {
 | 
				
			||||||
 | 
					        display: flex;
 | 
				
			||||||
 | 
					        flex-direction: column;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mx_CreateSubspaceDialog {
 | 
				
			||||||
 | 
					    width: 480px;
 | 
				
			||||||
 | 
					    color: $primary-fg-color;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    flex-direction: column;
 | 
				
			||||||
 | 
					    flex-wrap: nowrap;
 | 
				
			||||||
 | 
					    min-height: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_CreateSubspaceDialog_content {
 | 
				
			||||||
 | 
					        flex-grow: 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .mx_CreateSubspaceDialog_betaNotice {
 | 
				
			||||||
 | 
					            padding: 12px 16px;
 | 
				
			||||||
 | 
					            border-radius: 8px;
 | 
				
			||||||
 | 
					            background-color: $header-panel-bg-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .mx_BetaCard_betaPill {
 | 
				
			||||||
 | 
					                margin-right: 8px;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_CreateSubspaceDialog_footer {
 | 
				
			||||||
 | 
					        display: flex;
 | 
				
			||||||
 | 
					        margin-top: 20px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        > span {
 | 
				
			||||||
 | 
					            flex-grow: 1;
 | 
				
			||||||
 | 
					            font-size: $font-12px;
 | 
				
			||||||
 | 
					            line-height: $font-15px;
 | 
				
			||||||
 | 
					            color: $secondary-fg-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            > * {
 | 
				
			||||||
 | 
					                vertical-align: middle;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .mx_AccessibleButton {
 | 
				
			||||||
 | 
					            display: inline-block;
 | 
				
			||||||
 | 
					            align-self: center;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .mx_AccessibleButton_kind_primary {
 | 
				
			||||||
 | 
					            margin-left: 16px;
 | 
				
			||||||
 | 
					            padding: 8px 36px;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .mx_AccessibleButton_kind_link {
 | 
				
			||||||
 | 
					            padding: 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								res/css/views/dialogs/_JoinRuleDropdown.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								res/css/views/dialogs/_JoinRuleDropdown.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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_JoinRuleDropdown {
 | 
				
			||||||
 | 
					    margin-bottom: 8px;
 | 
				
			||||||
 | 
					    font-weight: normal;
 | 
				
			||||||
 | 
					    font-family: $font-family;
 | 
				
			||||||
 | 
					    font-size: $font-14px;
 | 
				
			||||||
 | 
					    color: $primary-fg-color;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_Dropdown_input {
 | 
				
			||||||
 | 
					        border: 1px solid $input-border-color;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_Dropdown_option {
 | 
				
			||||||
 | 
					        font-size: $font-14px;
 | 
				
			||||||
 | 
					        line-height: $font-32px;
 | 
				
			||||||
 | 
					        height: 32px;
 | 
				
			||||||
 | 
					        min-height: 32px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        > div {
 | 
				
			||||||
 | 
					            padding-left: 30px;
 | 
				
			||||||
 | 
					            position: relative;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            &::before {
 | 
				
			||||||
 | 
					                content: "";
 | 
				
			||||||
 | 
					                position: absolute;
 | 
				
			||||||
 | 
					                height: 16px;
 | 
				
			||||||
 | 
					                width: 16px;
 | 
				
			||||||
 | 
					                left: 6px;
 | 
				
			||||||
 | 
					                top: 8px;
 | 
				
			||||||
 | 
					                mask-repeat: no-repeat;
 | 
				
			||||||
 | 
					                mask-position: center;
 | 
				
			||||||
 | 
					                background-color: $secondary-fg-color;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_JoinRuleDropdown_invite::before {
 | 
				
			||||||
 | 
					        mask-image: url('$(res)/img/element-icons/lock.svg');
 | 
				
			||||||
 | 
					        mask-size: contain;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_JoinRuleDropdown_public::before {
 | 
				
			||||||
 | 
					        mask-image: url('$(res)/img/globe.svg');
 | 
				
			||||||
 | 
					        mask-size: 12px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .mx_JoinRuleDropdown_restricted::before {
 | 
				
			||||||
 | 
					        mask-image: url('$(res)/img/element-icons/community-members.svg');
 | 
				
			||||||
 | 
					        mask-size: contain;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,13 +47,24 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
 | 
				
			|||||||
import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
 | 
					import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
 | 
				
			||||||
import { useStateArray } from "../../hooks/useStateArray";
 | 
					import { useStateArray } from "../../hooks/useStateArray";
 | 
				
			||||||
import SpacePublicShare from "../views/spaces/SpacePublicShare";
 | 
					import SpacePublicShare from "../views/spaces/SpacePublicShare";
 | 
				
			||||||
import { shouldShowSpaceSettings, showAddExistingRooms, showCreateNewRoom, showSpaceSettings } from "../../utils/space";
 | 
					import {
 | 
				
			||||||
 | 
					    shouldShowSpaceSettings,
 | 
				
			||||||
 | 
					    showAddExistingRooms,
 | 
				
			||||||
 | 
					    showCreateNewRoom,
 | 
				
			||||||
 | 
					    showCreateNewSubspace,
 | 
				
			||||||
 | 
					    showSpaceSettings,
 | 
				
			||||||
 | 
					} 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 { 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 { AddExistingToSpace } from "../views/dialogs/AddExistingToSpaceDialog";
 | 
					import {
 | 
				
			||||||
 | 
					    AddExistingToSpace,
 | 
				
			||||||
 | 
					    defaultDmsRenderer,
 | 
				
			||||||
 | 
					    defaultRoomsRenderer,
 | 
				
			||||||
 | 
					    defaultSpacesRenderer,
 | 
				
			||||||
 | 
					} from "../views/dialogs/AddExistingToSpaceDialog";
 | 
				
			||||||
import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
 | 
					import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
 | 
				
			||||||
import IconizedContextMenu, {
 | 
					import IconizedContextMenu, {
 | 
				
			||||||
    IconizedContextMenuOption,
 | 
					    IconizedContextMenuOption,
 | 
				
			||||||
@@ -347,6 +358,22 @@ const SpaceLandingAddButton = ({ space, onNewRoomAdded }) => {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }}
 | 
					                    }}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
 | 
					                <IconizedContextMenuOption
 | 
				
			||||||
 | 
					                    label={_t("Add subspace")}
 | 
				
			||||||
 | 
					                    iconClassName="mx_RoomList_iconPlus"
 | 
				
			||||||
 | 
					                    onClick={async (e) => {
 | 
				
			||||||
 | 
					                        e.preventDefault();
 | 
				
			||||||
 | 
					                        e.stopPropagation();
 | 
				
			||||||
 | 
					                        closeMenu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        const [added] = await showCreateNewSubspace(space);
 | 
				
			||||||
 | 
					                        if (added) {
 | 
				
			||||||
 | 
					                            onNewRoomAdded();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <BetaPill />
 | 
				
			||||||
 | 
					                </IconizedContextMenuOption>
 | 
				
			||||||
            </IconizedContextMenuOptionList>
 | 
					            </IconizedContextMenuOptionList>
 | 
				
			||||||
        </IconizedContextMenu>;
 | 
					        </IconizedContextMenu>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -548,12 +575,13 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
 | 
				
			|||||||
                    { _t("Skip for now") }
 | 
					                    { _t("Skip for now") }
 | 
				
			||||||
                </AccessibleButton>
 | 
					                </AccessibleButton>
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            filterPlaceholder={_t("Search for rooms or spaces")}
 | 
				
			||||||
            onFinished={onFinished}
 | 
					            onFinished={onFinished}
 | 
				
			||||||
 | 
					            roomsRenderer={defaultRoomsRenderer}
 | 
				
			||||||
 | 
					            spacesRenderer={defaultSpacesRenderer}
 | 
				
			||||||
 | 
					            dmsRenderer={defaultDmsRenderer}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className="mx_SpaceRoomView_buttons">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <SpaceFeedbackPrompt />
 | 
					        <SpaceFeedbackPrompt />
 | 
				
			||||||
    </div>;
 | 
					    </div>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,10 +90,11 @@ export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
 | 
				
			|||||||
    </MenuItemCheckbox>;
 | 
					    </MenuItemCheckbox>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({ label, iconClassName, ...props }) => {
 | 
					export const IconizedContextMenuOption: React.FC<IOptionProps> = ({ label, iconClassName, children, ...props }) => {
 | 
				
			||||||
    return <MenuItem {...props} label={label}>
 | 
					    return <MenuItem {...props} label={label}>
 | 
				
			||||||
        { iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} /> }
 | 
					        { iconClassName && <span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} /> }
 | 
				
			||||||
        <span className="mx_IconizedContextMenu_label">{ label }</span>
 | 
					        <span className="mx_IconizedContextMenu_label">{ label }</span>
 | 
				
			||||||
 | 
					        { children }
 | 
				
			||||||
    </MenuItem>;
 | 
					    </MenuItem>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										70
									
								
								src/components/views/dialogs/AddExistingSubspaceDialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/components/views/dialogs/AddExistingSubspaceDialog.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React, { useState } from "react";
 | 
				
			||||||
 | 
					import { Room } from "matrix-js-sdk/src/models/room";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { _t } from '../../../languageHandler';
 | 
				
			||||||
 | 
					import BaseDialog from "./BaseDialog";
 | 
				
			||||||
 | 
					import AccessibleButton from "../elements/AccessibleButton";
 | 
				
			||||||
 | 
					import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
				
			||||||
 | 
					import { SpaceFeedbackPrompt } from "../../structures/SpaceRoomView";
 | 
				
			||||||
 | 
					import { AddExistingToSpace, defaultSpacesRenderer, SubspaceSelector } from "./AddExistingToSpaceDialog";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IProps {
 | 
				
			||||||
 | 
					    space: Room;
 | 
				
			||||||
 | 
					    onCreateSubspaceClick(): void;
 | 
				
			||||||
 | 
					    onFinished(added?: boolean): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AddExistingSubspaceDialog: React.FC<IProps> = ({ space, onCreateSubspaceClick, onFinished }) => {
 | 
				
			||||||
 | 
					    const [selectedSpace, setSelectedSpace] = useState(space);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <BaseDialog
 | 
				
			||||||
 | 
					        title={(
 | 
				
			||||||
 | 
					            <SubspaceSelector
 | 
				
			||||||
 | 
					                title={_t("Add existing space")}
 | 
				
			||||||
 | 
					                space={space}
 | 
				
			||||||
 | 
					                value={selectedSpace}
 | 
				
			||||||
 | 
					                onChange={setSelectedSpace}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        className="mx_AddExistingToSpaceDialog"
 | 
				
			||||||
 | 
					        contentId="mx_AddExistingToSpace"
 | 
				
			||||||
 | 
					        onFinished={onFinished}
 | 
				
			||||||
 | 
					        fixedWidth={false}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        <MatrixClientContext.Provider value={space.client}>
 | 
				
			||||||
 | 
					            <AddExistingToSpace
 | 
				
			||||||
 | 
					                space={space}
 | 
				
			||||||
 | 
					                onFinished={onFinished}
 | 
				
			||||||
 | 
					                footerPrompt={<>
 | 
				
			||||||
 | 
					                    <div>{ _t("Want to add a new space instead?") }</div>
 | 
				
			||||||
 | 
					                    <AccessibleButton onClick={onCreateSubspaceClick} kind="link">
 | 
				
			||||||
 | 
					                        { _t("Create a new subspace") }
 | 
				
			||||||
 | 
					                    </AccessibleButton>
 | 
				
			||||||
 | 
					                </>}
 | 
				
			||||||
 | 
					                filterPlaceholder={_t("Search for spaces")}
 | 
				
			||||||
 | 
					                spacesRenderer={defaultSpacesRenderer}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </MatrixClientContext.Provider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <SpaceFeedbackPrompt onClick={() => onFinished(false)} />
 | 
				
			||||||
 | 
					    </BaseDialog>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AddExistingSubspaceDialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,9 +18,9 @@ import React, { ReactNode, useContext, useMemo, useState } from "react";
 | 
				
			|||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
					import { Room } from "matrix-js-sdk/src/models/room";
 | 
				
			||||||
import { sleep } from "matrix-js-sdk/src/utils";
 | 
					import { sleep } from "matrix-js-sdk/src/utils";
 | 
				
			||||||
 | 
					import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { _t } from '../../../languageHandler';
 | 
					import { _t } from '../../../languageHandler';
 | 
				
			||||||
import { IDialogProps } from "./IDialogProps";
 | 
					 | 
				
			||||||
import BaseDialog from "./BaseDialog";
 | 
					import BaseDialog from "./BaseDialog";
 | 
				
			||||||
import Dropdown from "../elements/Dropdown";
 | 
					import Dropdown from "../elements/Dropdown";
 | 
				
			||||||
import SearchBox from "../../structures/SearchBox";
 | 
					import SearchBox from "../../structures/SearchBox";
 | 
				
			||||||
@@ -42,12 +42,14 @@ import TruncatedList from "../elements/TruncatedList";
 | 
				
			|||||||
import EntityTile from "../rooms/EntityTile";
 | 
					import EntityTile from "../rooms/EntityTile";
 | 
				
			||||||
import BaseAvatar from "../avatars/BaseAvatar";
 | 
					import BaseAvatar from "../avatars/BaseAvatar";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProps extends IDialogProps {
 | 
					interface IProps {
 | 
				
			||||||
    space: Room;
 | 
					    space: Room;
 | 
				
			||||||
    onCreateRoomClick(space: Room): void;
 | 
					    onCreateRoomClick(): void;
 | 
				
			||||||
 | 
					    onAddSubspaceClick(): void;
 | 
				
			||||||
 | 
					    onFinished(added?: boolean): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Entry = ({ room, checked, onChange }) => {
 | 
					export const Entry = ({ room, checked, onChange }) => {
 | 
				
			||||||
    return <label className="mx_AddExistingToSpace_entry">
 | 
					    return <label className="mx_AddExistingToSpace_entry">
 | 
				
			||||||
        { room?.isSpaceRoom()
 | 
					        { room?.isSpaceRoom()
 | 
				
			||||||
            ? <RoomAvatar room={room} height={32} width={32} />
 | 
					            ? <RoomAvatar room={room} height={32} width={32} />
 | 
				
			||||||
@@ -65,14 +67,36 @@ const Entry = ({ room, checked, onChange }) => {
 | 
				
			|||||||
interface IAddExistingToSpaceProps {
 | 
					interface IAddExistingToSpaceProps {
 | 
				
			||||||
    space: Room;
 | 
					    space: Room;
 | 
				
			||||||
    footerPrompt?: ReactNode;
 | 
					    footerPrompt?: ReactNode;
 | 
				
			||||||
 | 
					    filterPlaceholder: string;
 | 
				
			||||||
    emptySelectionButton?: ReactNode;
 | 
					    emptySelectionButton?: ReactNode;
 | 
				
			||||||
    onFinished(added: boolean): void;
 | 
					    onFinished(added: boolean): void;
 | 
				
			||||||
 | 
					    roomsRenderer?(
 | 
				
			||||||
 | 
					        rooms: Room[],
 | 
				
			||||||
 | 
					        selectedToAdd: Set<Room>,
 | 
				
			||||||
 | 
					        onChange: undefined | ((checked: boolean, room: Room) => void),
 | 
				
			||||||
 | 
					        truncateAt: number,
 | 
				
			||||||
 | 
					        overflowTile: (overflowCount: number, totalCount: number) => JSX.Element,
 | 
				
			||||||
 | 
					    ): ReactNode;
 | 
				
			||||||
 | 
					    spacesRenderer?(
 | 
				
			||||||
 | 
					        spaces: Room[],
 | 
				
			||||||
 | 
					        selectedToAdd: Set<Room>,
 | 
				
			||||||
 | 
					        onChange?: (checked: boolean, room: Room) => void,
 | 
				
			||||||
 | 
					    ): ReactNode;
 | 
				
			||||||
 | 
					    dmsRenderer?(
 | 
				
			||||||
 | 
					        dms: Room[],
 | 
				
			||||||
 | 
					        selectedToAdd: Set<Room>,
 | 
				
			||||||
 | 
					        onChange?: (checked: boolean, room: Room) => void,
 | 
				
			||||||
 | 
					    ): ReactNode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
 | 
					export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
 | 
				
			||||||
    space,
 | 
					    space,
 | 
				
			||||||
    footerPrompt,
 | 
					    footerPrompt,
 | 
				
			||||||
    emptySelectionButton,
 | 
					    emptySelectionButton,
 | 
				
			||||||
 | 
					    filterPlaceholder,
 | 
				
			||||||
 | 
					    roomsRenderer,
 | 
				
			||||||
 | 
					    dmsRenderer,
 | 
				
			||||||
 | 
					    spacesRenderer,
 | 
				
			||||||
    onFinished,
 | 
					    onFinished,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    const cli = useContext(MatrixClientContext);
 | 
					    const cli = useContext(MatrixClientContext);
 | 
				
			||||||
@@ -196,7 +220,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
 | 
				
			|||||||
        </>;
 | 
					        </>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onChange = !busy && !error ? (checked, room) => {
 | 
					    const onChange = !busy && !error ? (checked: boolean, room: Room) => {
 | 
				
			||||||
        if (checked) {
 | 
					        if (checked) {
 | 
				
			||||||
            selectedToAdd.add(room);
 | 
					            selectedToAdd.add(room);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -206,83 +230,52 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
 | 
				
			|||||||
    } : null;
 | 
					    } : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const [truncateAt, setTruncateAt] = useState(20);
 | 
					    const [truncateAt, setTruncateAt] = useState(20);
 | 
				
			||||||
    function overflowTile(overflowCount, totalCount) {
 | 
					    function overflowTile(overflowCount: number, totalCount: number): JSX.Element {
 | 
				
			||||||
        const text = _t("and %(count)s others...", { count: overflowCount });
 | 
					        const text = _t("and %(count)s others...", { count: overflowCount });
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
 | 
					            <EntityTile
 | 
				
			||||||
                <BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
 | 
					                className="mx_EntityTile_ellipsis"
 | 
				
			||||||
            } name={text} presenceState="online" suppressOnHover={true}
 | 
					                avatarJsx={
 | 
				
			||||||
            onClick={() => setTruncateAt(totalCount)} />
 | 
					                    <BaseAvatar url={require("../../../../res/img/ellipsis.svg")} name="..." width={36} height={36} />
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                name={text}
 | 
				
			||||||
 | 
					                presenceState="online"
 | 
				
			||||||
 | 
					                suppressOnHover={true}
 | 
				
			||||||
 | 
					                onClick={() => setTruncateAt(totalCount)}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let noResults = true;
 | 
				
			||||||
 | 
					    if ((roomsRenderer && rooms.length > 0) ||
 | 
				
			||||||
 | 
					        (dmsRenderer && dms.length > 0) ||
 | 
				
			||||||
 | 
					        (!roomsRenderer && !dmsRenderer && spacesRenderer && dms.length > 0) // only count spaces when alone
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        noResults = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return <div className="mx_AddExistingToSpace">
 | 
					    return <div className="mx_AddExistingToSpace">
 | 
				
			||||||
        <SearchBox
 | 
					        <SearchBox
 | 
				
			||||||
            className="mx_textinput_icon mx_textinput_search"
 | 
					            className="mx_textinput_icon mx_textinput_search"
 | 
				
			||||||
            placeholder={_t("Filter your rooms and spaces")}
 | 
					            placeholder={filterPlaceholder}
 | 
				
			||||||
            onSearch={setQuery}
 | 
					            onSearch={setQuery}
 | 
				
			||||||
            autoComplete={true}
 | 
					            autoComplete={true}
 | 
				
			||||||
            autoFocus={true}
 | 
					            autoFocus={true}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        <AutoHideScrollbar className="mx_AddExistingToSpace_content">
 | 
					        <AutoHideScrollbar className="mx_AddExistingToSpace_content">
 | 
				
			||||||
            { rooms.length > 0 ? (
 | 
					            { rooms.length > 0 && roomsRenderer ? (
 | 
				
			||||||
                <div className="mx_AddExistingToSpace_section">
 | 
					                roomsRenderer(rooms, selectedToAdd, onChange, truncateAt, overflowTile)
 | 
				
			||||||
                    <h3>{ _t("Rooms") }</h3>
 | 
					 | 
				
			||||||
                    <TruncatedList
 | 
					 | 
				
			||||||
                        truncateAt={truncateAt}
 | 
					 | 
				
			||||||
                        createOverflowElement={overflowTile}
 | 
					 | 
				
			||||||
                        getChildren={(start, end) => rooms.slice(start, end).map(room =>
 | 
					 | 
				
			||||||
                            <Entry
 | 
					 | 
				
			||||||
                                key={room.roomId}
 | 
					 | 
				
			||||||
                                room={room}
 | 
					 | 
				
			||||||
                                checked={selectedToAdd.has(room)}
 | 
					 | 
				
			||||||
                                onChange={onChange ? (checked) => {
 | 
					 | 
				
			||||||
                                    onChange(checked, room);
 | 
					 | 
				
			||||||
                                } : null}
 | 
					 | 
				
			||||||
                            />,
 | 
					 | 
				
			||||||
                        )}
 | 
					 | 
				
			||||||
                        getChildCount={() => rooms.length}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            ) : undefined }
 | 
					            ) : undefined }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            { spaces.length > 0 ? (
 | 
					            { spaces.length > 0 && spacesRenderer ? (
 | 
				
			||||||
                <div className="mx_AddExistingToSpace_section mx_AddExistingToSpace_section_spaces">
 | 
					                spacesRenderer(spaces, selectedToAdd, onChange)
 | 
				
			||||||
                    <h3>{ _t("Spaces") }</h3>
 | 
					 | 
				
			||||||
                    <div className="mx_AddExistingToSpace_section_experimental">
 | 
					 | 
				
			||||||
                        <div>{ _t("Feeling experimental?") }</div>
 | 
					 | 
				
			||||||
                        <div>{ _t("You can add existing spaces to a space.") }</div>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    { spaces.map(space => {
 | 
					 | 
				
			||||||
                        return <Entry
 | 
					 | 
				
			||||||
                            key={space.roomId}
 | 
					 | 
				
			||||||
                            room={space}
 | 
					 | 
				
			||||||
                            checked={selectedToAdd.has(space)}
 | 
					 | 
				
			||||||
                            onChange={onChange ? (checked) => {
 | 
					 | 
				
			||||||
                                onChange(checked, space);
 | 
					 | 
				
			||||||
                            } : null}
 | 
					 | 
				
			||||||
                        />;
 | 
					 | 
				
			||||||
                    }) }
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            ) : null }
 | 
					            ) : null }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            { dms.length > 0 ? (
 | 
					            { dms.length > 0 && dmsRenderer ? (
 | 
				
			||||||
                <div className="mx_AddExistingToSpace_section">
 | 
					                dmsRenderer(dms, selectedToAdd, onChange)
 | 
				
			||||||
                    <h3>{ _t("Direct Messages") }</h3>
 | 
					 | 
				
			||||||
                    { dms.map(room => {
 | 
					 | 
				
			||||||
                        return <Entry
 | 
					 | 
				
			||||||
                            key={room.roomId}
 | 
					 | 
				
			||||||
                            room={room}
 | 
					 | 
				
			||||||
                            checked={selectedToAdd.has(room)}
 | 
					 | 
				
			||||||
                            onChange={onChange ? (checked) => {
 | 
					 | 
				
			||||||
                                onChange(checked, room);
 | 
					 | 
				
			||||||
                            } : null}
 | 
					 | 
				
			||||||
                        />;
 | 
					 | 
				
			||||||
                    }) }
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            ) : null }
 | 
					            ) : null }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            { spaces.length + rooms.length + dms.length < 1 ? <span className="mx_AddExistingToSpace_noResults">
 | 
					            { noResults ? <span className="mx_AddExistingToSpace_noResults">
 | 
				
			||||||
                { _t("No results") }
 | 
					                { _t("No results") }
 | 
				
			||||||
            </span> : undefined }
 | 
					            </span> : undefined }
 | 
				
			||||||
        </AutoHideScrollbar>
 | 
					        </AutoHideScrollbar>
 | 
				
			||||||
@@ -293,50 +286,126 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
 | 
				
			|||||||
    </div>;
 | 
					    </div>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick, onFinished }) => {
 | 
					export const defaultRoomsRenderer: IAddExistingToSpaceProps["roomsRenderer"] = (
 | 
				
			||||||
    const [selectedSpace, setSelectedSpace] = useState(space);
 | 
					    rooms, selectedToAdd, onChange, truncateAt, overflowTile,
 | 
				
			||||||
    const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
 | 
					) => (
 | 
				
			||||||
 | 
					    <div className="mx_AddExistingToSpace_section">
 | 
				
			||||||
 | 
					        <h3>{ _t("Rooms") }</h3>
 | 
				
			||||||
 | 
					        <TruncatedList
 | 
				
			||||||
 | 
					            truncateAt={truncateAt}
 | 
				
			||||||
 | 
					            createOverflowElement={overflowTile}
 | 
				
			||||||
 | 
					            getChildren={(start, end) => rooms.slice(start, end).map(room =>
 | 
				
			||||||
 | 
					                <Entry
 | 
				
			||||||
 | 
					                    key={room.roomId}
 | 
				
			||||||
 | 
					                    room={room}
 | 
				
			||||||
 | 
					                    checked={selectedToAdd.has(room)}
 | 
				
			||||||
 | 
					                    onChange={onChange ? (checked: boolean) => {
 | 
				
			||||||
 | 
					                        onChange(checked, room);
 | 
				
			||||||
 | 
					                    } : null}
 | 
				
			||||||
 | 
					                />,
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					            getChildCount={() => rooms.length}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let spaceOptionSection;
 | 
					export const defaultSpacesRenderer: IAddExistingToSpaceProps["spacesRenderer"] = (spaces, selectedToAdd, onChange) => (
 | 
				
			||||||
    if (existingSubspaces.length > 0) {
 | 
					    <div className="mx_AddExistingToSpace_section">
 | 
				
			||||||
        const options = [space, ...existingSubspaces].map((space) => {
 | 
					        { spaces.map(space => {
 | 
				
			||||||
            const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", {
 | 
					            return <Entry
 | 
				
			||||||
                mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace,
 | 
					                key={space.roomId}
 | 
				
			||||||
            });
 | 
					                room={space}
 | 
				
			||||||
            return <div key={space.roomId} className={classes}>
 | 
					                checked={selectedToAdd.has(space)}
 | 
				
			||||||
                <RoomAvatar room={space} width={24} height={24} />
 | 
					                onChange={onChange ? (checked) => {
 | 
				
			||||||
                { space.name || getDisplayAliasForRoom(space) || space.roomId }
 | 
					                    onChange(checked, space);
 | 
				
			||||||
            </div>;
 | 
					                } : null}
 | 
				
			||||||
        });
 | 
					            />;
 | 
				
			||||||
 | 
					        }) }
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        spaceOptionSection = (
 | 
					export const defaultDmsRenderer: IAddExistingToSpaceProps["dmsRenderer"] = (dms, selectedToAdd, onChange) => (
 | 
				
			||||||
 | 
					    <div className="mx_AddExistingToSpace_section">
 | 
				
			||||||
 | 
					        <h3>{ _t("Direct Messages") }</h3>
 | 
				
			||||||
 | 
					        { dms.map(room => {
 | 
				
			||||||
 | 
					            return <Entry
 | 
				
			||||||
 | 
					                key={room.roomId}
 | 
				
			||||||
 | 
					                room={room}
 | 
				
			||||||
 | 
					                checked={selectedToAdd.has(room)}
 | 
				
			||||||
 | 
					                onChange={onChange ? (checked: boolean) => {
 | 
				
			||||||
 | 
					                    onChange(checked, room);
 | 
				
			||||||
 | 
					                } : null}
 | 
				
			||||||
 | 
					            />;
 | 
				
			||||||
 | 
					        }) }
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ISubspaceSelectorProps {
 | 
				
			||||||
 | 
					    title: string;
 | 
				
			||||||
 | 
					    space: Room;
 | 
				
			||||||
 | 
					    value: Room;
 | 
				
			||||||
 | 
					    onChange(space: Room): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SubspaceSelector = ({ title, space, value, onChange }: ISubspaceSelectorProps) => {
 | 
				
			||||||
 | 
					    const options = useMemo(() => {
 | 
				
			||||||
 | 
					        return [space, ...SpaceStore.instance.getChildSpaces(space.roomId).filter(space => {
 | 
				
			||||||
 | 
					            return space.currentState.maySendStateEvent(EventType.SpaceChild, space.client.credentials.userId);
 | 
				
			||||||
 | 
					        })];
 | 
				
			||||||
 | 
					    }, [space]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let body;
 | 
				
			||||||
 | 
					    if (options.length > 1) {
 | 
				
			||||||
 | 
					        body = (
 | 
				
			||||||
            <Dropdown
 | 
					            <Dropdown
 | 
				
			||||||
                id="mx_SpaceSelectDropdown"
 | 
					                id="mx_SpaceSelectDropdown"
 | 
				
			||||||
 | 
					                className="mx_SpaceSelectDropdown"
 | 
				
			||||||
                onOptionChange={(key: string) => {
 | 
					                onOptionChange={(key: string) => {
 | 
				
			||||||
                    setSelectedSpace(existingSubspaces.find(space => space.roomId === key) || space);
 | 
					                    onChange(options.find(space => space.roomId === key) || space);
 | 
				
			||||||
                }}
 | 
					                }}
 | 
				
			||||||
                value={selectedSpace.roomId}
 | 
					                value={value.roomId}
 | 
				
			||||||
                label={_t("Space selection")}
 | 
					                label={_t("Space selection")}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                { options }
 | 
					                { options.map((space) => {
 | 
				
			||||||
 | 
					                    const classes = classNames({
 | 
				
			||||||
 | 
					                        mx_SubspaceSelector_dropdownOptionActive: space === value,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    return <div key={space.roomId} className={classes}>
 | 
				
			||||||
 | 
					                        <RoomAvatar room={space} width={24} height={24} />
 | 
				
			||||||
 | 
					                        { space.name || getDisplayAliasForRoom(space) || space.roomId }
 | 
				
			||||||
 | 
					                    </div>;
 | 
				
			||||||
 | 
					                }) }
 | 
				
			||||||
            </Dropdown>
 | 
					            </Dropdown>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        spaceOptionSection = <div className="mx_AddExistingToSpaceDialog_onlySpace">
 | 
					        body = (
 | 
				
			||||||
            { space.name || getDisplayAliasForRoom(space) || space.roomId }
 | 
					            <div className="mx_SubspaceSelector_onlySpace">
 | 
				
			||||||
        </div>;
 | 
					                { space.name || getDisplayAliasForRoom(space) || space.roomId }
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const title = <React.Fragment>
 | 
					    return <div className="mx_SubspaceSelector">
 | 
				
			||||||
        <RoomAvatar room={selectedSpace} height={40} width={40} />
 | 
					        <RoomAvatar room={value} height={40} width={40} />
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <h1>{ _t("Add existing rooms") }</h1>
 | 
					            <h1>{ title }</h1>
 | 
				
			||||||
            { spaceOptionSection }
 | 
					            { body }
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </React.Fragment>;
 | 
					    </div>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick, onAddSubspaceClick, onFinished }) => {
 | 
				
			||||||
 | 
					    const [selectedSpace, setSelectedSpace] = useState(space);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return <BaseDialog
 | 
					    return <BaseDialog
 | 
				
			||||||
        title={title}
 | 
					        title={(
 | 
				
			||||||
 | 
					            <SubspaceSelector
 | 
				
			||||||
 | 
					                title={_t("Add existing rooms")}
 | 
				
			||||||
 | 
					                space={space}
 | 
				
			||||||
 | 
					                value={selectedSpace}
 | 
				
			||||||
 | 
					                onChange={setSelectedSpace}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
        className="mx_AddExistingToSpaceDialog"
 | 
					        className="mx_AddExistingToSpaceDialog"
 | 
				
			||||||
        contentId="mx_AddExistingToSpace"
 | 
					        contentId="mx_AddExistingToSpace"
 | 
				
			||||||
        onFinished={onFinished}
 | 
					        onFinished={onFinished}
 | 
				
			||||||
@@ -348,10 +417,27 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
 | 
				
			|||||||
                onFinished={onFinished}
 | 
					                onFinished={onFinished}
 | 
				
			||||||
                footerPrompt={<>
 | 
					                footerPrompt={<>
 | 
				
			||||||
                    <div>{ _t("Want to add a new room instead?") }</div>
 | 
					                    <div>{ _t("Want to add a new room instead?") }</div>
 | 
				
			||||||
                    <AccessibleButton onClick={() => onCreateRoomClick(space)} kind="link">
 | 
					                    <AccessibleButton kind="link" onClick={() => {
 | 
				
			||||||
 | 
					                        onCreateRoomClick();
 | 
				
			||||||
 | 
					                        onFinished();
 | 
				
			||||||
 | 
					                    }}>
 | 
				
			||||||
                        { _t("Create a new room") }
 | 
					                        { _t("Create a new room") }
 | 
				
			||||||
                    </AccessibleButton>
 | 
					                    </AccessibleButton>
 | 
				
			||||||
                </>}
 | 
					                </>}
 | 
				
			||||||
 | 
					                filterPlaceholder={_t("Search for rooms")}
 | 
				
			||||||
 | 
					                roomsRenderer={defaultRoomsRenderer}
 | 
				
			||||||
 | 
					                spacesRenderer={() => (
 | 
				
			||||||
 | 
					                    <div className="mx_AddExistingToSpace_section">
 | 
				
			||||||
 | 
					                        <h3>{ _t("Spaces") }</h3>
 | 
				
			||||||
 | 
					                        <AccessibleButton kind="link" onClick={() => {
 | 
				
			||||||
 | 
					                            onAddSubspaceClick();
 | 
				
			||||||
 | 
					                            onFinished();
 | 
				
			||||||
 | 
					                        }}>
 | 
				
			||||||
 | 
					                            { _t("Adding spaces has moved.") }
 | 
				
			||||||
 | 
					                        </AccessibleButton>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					                dmsRenderer={defaultDmsRenderer}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
        </MatrixClientContext.Provider>
 | 
					        </MatrixClientContext.Provider>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,8 +32,8 @@ import RoomAliasField from "../elements/RoomAliasField";
 | 
				
			|||||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
 | 
					import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
 | 
				
			||||||
import DialogButtons from "../elements/DialogButtons";
 | 
					import DialogButtons from "../elements/DialogButtons";
 | 
				
			||||||
import BaseDialog from "../dialogs/BaseDialog";
 | 
					import BaseDialog from "../dialogs/BaseDialog";
 | 
				
			||||||
import Dropdown from "../elements/Dropdown";
 | 
					 | 
				
			||||||
import SpaceStore from "../../../stores/SpaceStore";
 | 
					import SpaceStore from "../../../stores/SpaceStore";
 | 
				
			||||||
 | 
					import JoinRuleDropdown from "../elements/JoinRuleDropdown";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProps {
 | 
					interface IProps {
 | 
				
			||||||
    defaultPublic?: boolean;
 | 
					    defaultPublic?: boolean;
 | 
				
			||||||
@@ -321,21 +321,6 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
 | 
				
			|||||||
            title = this.state.joinRule === JoinRule.Public ? _t('Create a public room') : _t('Create a private room');
 | 
					            title = this.state.joinRule === JoinRule.Public ? _t('Create a public room') : _t('Create a private room');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const options = [
 | 
					 | 
				
			||||||
            <div key={JoinRule.Invite} className="mx_CreateRoomDialog_dropdown_invite">
 | 
					 | 
				
			||||||
                { _t("Private room (invite only)") }
 | 
					 | 
				
			||||||
            </div>,
 | 
					 | 
				
			||||||
            <div key={JoinRule.Public} className="mx_CreateRoomDialog_dropdown_public">
 | 
					 | 
				
			||||||
                { _t("Public room") }
 | 
					 | 
				
			||||||
            </div>,
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.supportsRestricted) {
 | 
					 | 
				
			||||||
            options.unshift(<div key={JoinRule.Restricted} className="mx_CreateRoomDialog_dropdown_restricted">
 | 
					 | 
				
			||||||
                { _t("Visible to space members") }
 | 
					 | 
				
			||||||
            </div>);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished} title={title}>
 | 
					            <BaseDialog className="mx_CreateRoomDialog" onFinished={this.props.onFinished} title={title}>
 | 
				
			||||||
                <form onSubmit={this.onOk} onKeyDown={this.onKeyDown}>
 | 
					                <form onSubmit={this.onOk} onKeyDown={this.onKeyDown}>
 | 
				
			||||||
@@ -355,16 +340,14 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
 | 
				
			|||||||
                            className="mx_CreateRoomDialog_topic"
 | 
					                            className="mx_CreateRoomDialog_topic"
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <Dropdown
 | 
					                        <JoinRuleDropdown
 | 
				
			||||||
                            id="mx_CreateRoomDialog_typeDropdown"
 | 
					 | 
				
			||||||
                            className="mx_CreateRoomDialog_typeDropdown"
 | 
					 | 
				
			||||||
                            onOptionChange={this.onJoinRuleChange}
 | 
					 | 
				
			||||||
                            menuWidth={448}
 | 
					 | 
				
			||||||
                            value={this.state.joinRule}
 | 
					 | 
				
			||||||
                            label={_t("Room visibility")}
 | 
					                            label={_t("Room visibility")}
 | 
				
			||||||
                        >
 | 
					                            labelInvite={_t("Private room (invite only)")}
 | 
				
			||||||
                            { options }
 | 
					                            labelPublic={_t("Public room")}
 | 
				
			||||||
                        </Dropdown>
 | 
					                            labelRestricted={this.supportsRestricted ? _t("Visible to space members") : undefined}
 | 
				
			||||||
 | 
					                            value={this.state.joinRule}
 | 
				
			||||||
 | 
					                            onChange={this.onJoinRuleChange}
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        { publicPrivateLabel }
 | 
					                        { publicPrivateLabel }
 | 
				
			||||||
                        { e2eeSection }
 | 
					                        { e2eeSection }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										183
									
								
								src/components/views/dialogs/CreateSubspaceDialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/components/views/dialogs/CreateSubspaceDialog.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React, { useRef, useState } from "react";
 | 
				
			||||||
 | 
					import { Room } from "matrix-js-sdk/src/models/room";
 | 
				
			||||||
 | 
					import { JoinRule, Preset } from "matrix-js-sdk/src/@types/partials";
 | 
				
			||||||
 | 
					import { RoomType } from "matrix-js-sdk/src/@types/event";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { _t } from '../../../languageHandler';
 | 
				
			||||||
 | 
					import BaseDialog from "./BaseDialog";
 | 
				
			||||||
 | 
					import AccessibleButton from "../elements/AccessibleButton";
 | 
				
			||||||
 | 
					import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
				
			||||||
 | 
					import { BetaPill } from "../beta/BetaCard";
 | 
				
			||||||
 | 
					import Field from "../elements/Field";
 | 
				
			||||||
 | 
					import RoomAliasField from "../elements/RoomAliasField";
 | 
				
			||||||
 | 
					import SpaceStore from "../../../stores/SpaceStore";
 | 
				
			||||||
 | 
					import { SpaceCreateForm } from "../spaces/SpaceCreateMenu";
 | 
				
			||||||
 | 
					import createRoom from "../../../createRoom";
 | 
				
			||||||
 | 
					import { SubspaceSelector } from "./AddExistingToSpaceDialog";
 | 
				
			||||||
 | 
					import JoinRuleDropdown from "../elements/JoinRuleDropdown";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IProps {
 | 
				
			||||||
 | 
					    space: Room;
 | 
				
			||||||
 | 
					    onAddExistingSpaceClick(): void;
 | 
				
			||||||
 | 
					    onFinished(added?: boolean): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick, onFinished }) => {
 | 
				
			||||||
 | 
					    const [parentSpace, setParentSpace] = useState(space);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const [busy, setBusy] = useState<boolean>(false);
 | 
				
			||||||
 | 
					    const [name, setName] = useState("");
 | 
				
			||||||
 | 
					    const spaceNameField = useRef<Field>();
 | 
				
			||||||
 | 
					    const [alias, setAlias] = useState("");
 | 
				
			||||||
 | 
					    const spaceAliasField = useRef<RoomAliasField>();
 | 
				
			||||||
 | 
					    const [avatar, setAvatar] = useState<File>(null);
 | 
				
			||||||
 | 
					    const [topic, setTopic] = useState<string>("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const supportsRestricted = !!SpaceStore.instance.restrictedJoinRuleSupport?.preferred;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const spaceJoinRule = space.getJoinRule();
 | 
				
			||||||
 | 
					    let defaultJoinRule = JoinRule.Invite;
 | 
				
			||||||
 | 
					    if (spaceJoinRule === JoinRule.Public) {
 | 
				
			||||||
 | 
					        defaultJoinRule = JoinRule.Public;
 | 
				
			||||||
 | 
					    } else if (supportsRestricted) {
 | 
				
			||||||
 | 
					        defaultJoinRule = JoinRule.Restricted;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const [joinRule, setJoinRule] = useState<JoinRule>(defaultJoinRule);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onCreateSubspaceClick = async (e) => {
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					        if (busy) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setBusy(true);
 | 
				
			||||||
 | 
					        // require & validate the space name field
 | 
				
			||||||
 | 
					        if (!await spaceNameField.current.validate({ allowEmpty: false })) {
 | 
				
			||||||
 | 
					            spaceNameField.current.focus();
 | 
				
			||||||
 | 
					            spaceNameField.current.validate({ allowEmpty: false, focused: true });
 | 
				
			||||||
 | 
					            setBusy(false);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // validate the space name alias field but do not require it
 | 
				
			||||||
 | 
					        if (joinRule === JoinRule.Public && !await spaceAliasField.current.validate({ allowEmpty: true })) {
 | 
				
			||||||
 | 
					            spaceAliasField.current.focus();
 | 
				
			||||||
 | 
					            spaceAliasField.current.validate({ allowEmpty: true, focused: true });
 | 
				
			||||||
 | 
					            setBusy(false);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await createRoom({
 | 
				
			||||||
 | 
					                createOpts: {
 | 
				
			||||||
 | 
					                    preset: joinRule === JoinRule.Public ? Preset.PublicChat : Preset.PrivateChat,
 | 
				
			||||||
 | 
					                    name,
 | 
				
			||||||
 | 
					                    power_level_content_override: {
 | 
				
			||||||
 | 
					                        // Only allow Admins to write to the timeline to prevent hidden sync spam
 | 
				
			||||||
 | 
					                        events_default: 100,
 | 
				
			||||||
 | 
					                        ...joinRule === JoinRule.Public ? { invite: 0 } : {},
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    room_alias_name: joinRule === JoinRule.Public && alias
 | 
				
			||||||
 | 
					                        ? alias.substr(1, alias.indexOf(":") - 1)
 | 
				
			||||||
 | 
					                        : undefined,
 | 
				
			||||||
 | 
					                    topic,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                avatar,
 | 
				
			||||||
 | 
					                roomType: RoomType.Space,
 | 
				
			||||||
 | 
					                parentSpace,
 | 
				
			||||||
 | 
					                spinner: false,
 | 
				
			||||||
 | 
					                encryption: false,
 | 
				
			||||||
 | 
					                andView: true,
 | 
				
			||||||
 | 
					                inlineErrors: true,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            onFinished(true);
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            console.error(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <BaseDialog
 | 
				
			||||||
 | 
					        title={(
 | 
				
			||||||
 | 
					            <SubspaceSelector
 | 
				
			||||||
 | 
					                title={_t("Create a subspace")}
 | 
				
			||||||
 | 
					                space={space}
 | 
				
			||||||
 | 
					                value={parentSpace}
 | 
				
			||||||
 | 
					                onChange={setParentSpace}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					        className="mx_CreateSubspaceDialog"
 | 
				
			||||||
 | 
					        contentId="mx_CreateSubspaceDialog"
 | 
				
			||||||
 | 
					        onFinished={onFinished}
 | 
				
			||||||
 | 
					        fixedWidth={false}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        <MatrixClientContext.Provider value={space.client}>
 | 
				
			||||||
 | 
					            <div className="mx_CreateSubspaceDialog_content">
 | 
				
			||||||
 | 
					                <div className="mx_CreateSubspaceDialog_betaNotice">
 | 
				
			||||||
 | 
					                    <BetaPill />
 | 
				
			||||||
 | 
					                    { _t("Add a subspace to a space you manage.") }
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <SpaceCreateForm
 | 
				
			||||||
 | 
					                    busy={busy}
 | 
				
			||||||
 | 
					                    onSubmit={onCreateSubspaceClick}
 | 
				
			||||||
 | 
					                    setAvatar={setAvatar}
 | 
				
			||||||
 | 
					                    name={name}
 | 
				
			||||||
 | 
					                    setName={setName}
 | 
				
			||||||
 | 
					                    nameFieldRef={spaceNameField}
 | 
				
			||||||
 | 
					                    topic={topic}
 | 
				
			||||||
 | 
					                    setTopic={setTopic}
 | 
				
			||||||
 | 
					                    alias={alias}
 | 
				
			||||||
 | 
					                    setAlias={setAlias}
 | 
				
			||||||
 | 
					                    showAliasField={joinRule === JoinRule.Public}
 | 
				
			||||||
 | 
					                    aliasFieldRef={spaceAliasField}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <JoinRuleDropdown
 | 
				
			||||||
 | 
					                        label={_t("Subspace visibility")}
 | 
				
			||||||
 | 
					                        labelInvite={_t("Private subspace (invite only)")}
 | 
				
			||||||
 | 
					                        labelPublic={_t("Public subspace")}
 | 
				
			||||||
 | 
					                        labelRestricted={supportsRestricted ? _t("Visible to space members") : undefined}
 | 
				
			||||||
 | 
					                        width={478}
 | 
				
			||||||
 | 
					                        value={joinRule}
 | 
				
			||||||
 | 
					                        onChange={setJoinRule}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                </SpaceCreateForm>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div className="mx_CreateSubspaceDialog_footer">
 | 
				
			||||||
 | 
					                <span>
 | 
				
			||||||
 | 
					                    <div>{ _t("Want to add an existing space instead?") }</div>
 | 
				
			||||||
 | 
					                    <AccessibleButton kind="link" onClick={() => {
 | 
				
			||||||
 | 
					                        onAddExistingSpaceClick();
 | 
				
			||||||
 | 
					                        onFinished();
 | 
				
			||||||
 | 
					                    }}>
 | 
				
			||||||
 | 
					                        { _t("Add existing space") }
 | 
				
			||||||
 | 
					                    </AccessibleButton>
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <AccessibleButton kind="primary_outline" disabled={busy} onClick={() => onFinished(false)}>
 | 
				
			||||||
 | 
					                    { _t("Cancel") }
 | 
				
			||||||
 | 
					                </AccessibleButton>
 | 
				
			||||||
 | 
					                <AccessibleButton kind="primary" disabled={busy} onClick={onCreateSubspaceClick}>
 | 
				
			||||||
 | 
					                    { busy ? _t("Adding...") : _t("Add") }
 | 
				
			||||||
 | 
					                </AccessibleButton>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </MatrixClientContext.Provider>
 | 
				
			||||||
 | 
					    </BaseDialog>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default CreateSubspaceDialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/components/views/elements/JoinRuleDropdown.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/components/views/elements/JoinRuleDropdown.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { JoinRule } from 'matrix-js-sdk/src/@types/partials';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Dropdown from "./Dropdown";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IProps {
 | 
				
			||||||
 | 
					    value: JoinRule;
 | 
				
			||||||
 | 
					    label: string;
 | 
				
			||||||
 | 
					    width?: number;
 | 
				
			||||||
 | 
					    labelInvite: string;
 | 
				
			||||||
 | 
					    labelPublic: string;
 | 
				
			||||||
 | 
					    labelRestricted?: string; // if omitted then this option will be hidden, e.g if unsupported
 | 
				
			||||||
 | 
					    onChange(value: JoinRule): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const JoinRuleDropdown = ({
 | 
				
			||||||
 | 
					    label,
 | 
				
			||||||
 | 
					    labelInvite,
 | 
				
			||||||
 | 
					    labelPublic,
 | 
				
			||||||
 | 
					    labelRestricted,
 | 
				
			||||||
 | 
					    value,
 | 
				
			||||||
 | 
					    width = 448,
 | 
				
			||||||
 | 
					    onChange,
 | 
				
			||||||
 | 
					}: IProps) => {
 | 
				
			||||||
 | 
					    const options = [
 | 
				
			||||||
 | 
					        <div key={JoinRule.Invite} className="mx_JoinRuleDropdown_invite">
 | 
				
			||||||
 | 
					            { labelInvite }
 | 
				
			||||||
 | 
					        </div>,
 | 
				
			||||||
 | 
					        <div key={JoinRule.Public} className="mx_JoinRuleDropdown_public">
 | 
				
			||||||
 | 
					            { labelPublic }
 | 
				
			||||||
 | 
					        </div>,
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (labelRestricted) {
 | 
				
			||||||
 | 
					        options.unshift(<div key={JoinRule.Restricted} className="mx_JoinRuleDropdown_restricted">
 | 
				
			||||||
 | 
					            { labelRestricted }
 | 
				
			||||||
 | 
					        </div>);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <Dropdown
 | 
				
			||||||
 | 
					        id="mx_JoinRuleDropdown"
 | 
				
			||||||
 | 
					        className="mx_JoinRuleDropdown"
 | 
				
			||||||
 | 
					        onOptionChange={onChange}
 | 
				
			||||||
 | 
					        menuWidth={width}
 | 
				
			||||||
 | 
					        value={value}
 | 
				
			||||||
 | 
					        label={label}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					        { options }
 | 
				
			||||||
 | 
					    </Dropdown>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default JoinRuleDropdown;
 | 
				
			||||||
@@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
 | 
				
			|||||||
limitations under the License.
 | 
					limitations under the License.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import React, { useContext, useRef, useState } from "react";
 | 
					import React, { ComponentProps, RefObject, SyntheticEvent, useContext, useRef, useState } from "react";
 | 
				
			||||||
import classNames from "classnames";
 | 
					import classNames from "classnames";
 | 
				
			||||||
import { EventType, RoomType, RoomCreateTypeField } from "matrix-js-sdk/src/@types/event";
 | 
					import { RoomType } from "matrix-js-sdk/src/@types/event";
 | 
				
			||||||
import FocusLock from "react-focus-lock";
 | 
					import FocusLock from "react-focus-lock";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { _t } from "../../../languageHandler";
 | 
					import { _t } from "../../../languageHandler";
 | 
				
			||||||
@@ -24,7 +24,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
 | 
				
			|||||||
import { ChevronFace, ContextMenu } from "../../structures/ContextMenu";
 | 
					import { ChevronFace, ContextMenu } from "../../structures/ContextMenu";
 | 
				
			||||||
import createRoom from "../../../createRoom";
 | 
					import createRoom from "../../../createRoom";
 | 
				
			||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
					import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
				
			||||||
import { SpaceAvatar } from "./SpaceBasicSettings";
 | 
					import SpaceBasicSettings, { SpaceAvatar } from "./SpaceBasicSettings";
 | 
				
			||||||
import AccessibleButton from "../elements/AccessibleButton";
 | 
					import AccessibleButton from "../elements/AccessibleButton";
 | 
				
			||||||
import { BetaPill } from "../beta/BetaCard";
 | 
					import { BetaPill } from "../beta/BetaCard";
 | 
				
			||||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
 | 
					import defaultDispatcher from "../../../dispatcher/dispatcher";
 | 
				
			||||||
@@ -33,8 +33,7 @@ import { UserTab } from "../dialogs/UserSettingsDialog";
 | 
				
			|||||||
import Field from "../elements/Field";
 | 
					import Field from "../elements/Field";
 | 
				
			||||||
import withValidation from "../elements/Validation";
 | 
					import withValidation from "../elements/Validation";
 | 
				
			||||||
import { SpaceFeedbackPrompt } from "../../structures/SpaceRoomView";
 | 
					import { SpaceFeedbackPrompt } from "../../structures/SpaceRoomView";
 | 
				
			||||||
import { Preset } from "matrix-js-sdk/src/@types/partials";
 | 
					import { HistoryVisibility, Preset } from "matrix-js-sdk/src/@types/partials";
 | 
				
			||||||
import { ICreateRoomStateEvent } from "matrix-js-sdk/src/@types/requests";
 | 
					 | 
				
			||||||
import RoomAliasField from "../elements/RoomAliasField";
 | 
					import RoomAliasField from "../elements/RoomAliasField";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
 | 
					const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
 | 
				
			||||||
@@ -66,8 +65,83 @@ const nameToAlias = (name: string, domain: string): string => {
 | 
				
			|||||||
    return `#${localpart}:${domain}`;
 | 
					    return `#${localpart}:${domain}`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SpaceCreateMenu = ({ onFinished }) => {
 | 
					type BProps = Pick<ComponentProps<typeof SpaceBasicSettings>, "setAvatar" | "name" | "setName" | "topic" | "setTopic">;
 | 
				
			||||||
 | 
					interface ISpaceCreateFormProps extends BProps {
 | 
				
			||||||
 | 
					    busy: boolean;
 | 
				
			||||||
 | 
					    alias: string;
 | 
				
			||||||
 | 
					    nameFieldRef: RefObject<Field>;
 | 
				
			||||||
 | 
					    aliasFieldRef: RefObject<RoomAliasField>;
 | 
				
			||||||
 | 
					    showAliasField?: boolean;
 | 
				
			||||||
 | 
					    onSubmit(e: SyntheticEvent): void;
 | 
				
			||||||
 | 
					    setAlias(alias: string): void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
 | 
				
			||||||
 | 
					    busy,
 | 
				
			||||||
 | 
					    onSubmit,
 | 
				
			||||||
 | 
					    setAvatar,
 | 
				
			||||||
 | 
					    name,
 | 
				
			||||||
 | 
					    setName,
 | 
				
			||||||
 | 
					    nameFieldRef,
 | 
				
			||||||
 | 
					    alias,
 | 
				
			||||||
 | 
					    aliasFieldRef,
 | 
				
			||||||
 | 
					    setAlias,
 | 
				
			||||||
 | 
					    showAliasField,
 | 
				
			||||||
 | 
					    topic,
 | 
				
			||||||
 | 
					    setTopic,
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
    const cli = useContext(MatrixClientContext);
 | 
					    const cli = useContext(MatrixClientContext);
 | 
				
			||||||
 | 
					    const domain = cli.getDomain();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return <form className="mx_SpaceBasicSettings" onSubmit={onSubmit}>
 | 
				
			||||||
 | 
					        <SpaceAvatar setAvatar={setAvatar} avatarDisabled={busy} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Field
 | 
				
			||||||
 | 
					            name="spaceName"
 | 
				
			||||||
 | 
					            label={_t("Name")}
 | 
				
			||||||
 | 
					            autoFocus={true}
 | 
				
			||||||
 | 
					            value={name}
 | 
				
			||||||
 | 
					            onChange={ev => {
 | 
				
			||||||
 | 
					                const newName = ev.target.value;
 | 
				
			||||||
 | 
					                if (!alias || alias === nameToAlias(name, domain)) {
 | 
				
			||||||
 | 
					                    setAlias(nameToAlias(newName, domain));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                setName(newName);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            ref={nameFieldRef}
 | 
				
			||||||
 | 
					            onValidate={spaceNameValidator}
 | 
				
			||||||
 | 
					            disabled={busy}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        { showAliasField
 | 
				
			||||||
 | 
					            ? <RoomAliasField
 | 
				
			||||||
 | 
					                ref={aliasFieldRef}
 | 
				
			||||||
 | 
					                onChange={setAlias}
 | 
				
			||||||
 | 
					                domain={domain}
 | 
				
			||||||
 | 
					                value={alias}
 | 
				
			||||||
 | 
					                placeholder={name ? nameToAlias(name, domain) : _t("e.g. my-space")}
 | 
				
			||||||
 | 
					                label={_t("Address")}
 | 
				
			||||||
 | 
					                disabled={busy}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            : null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Field
 | 
				
			||||||
 | 
					            name="spaceTopic"
 | 
				
			||||||
 | 
					            element="textarea"
 | 
				
			||||||
 | 
					            label={_t("Description")}
 | 
				
			||||||
 | 
					            value={topic}
 | 
				
			||||||
 | 
					            onChange={ev => setTopic(ev.target.value)}
 | 
				
			||||||
 | 
					            rows={3}
 | 
				
			||||||
 | 
					            disabled={busy}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        { children }
 | 
				
			||||||
 | 
					    </form>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const SpaceCreateMenu = ({ onFinished }) => {
 | 
				
			||||||
    const [visibility, setVisibility] = useState<Visibility>(null);
 | 
					    const [visibility, setVisibility] = useState<Visibility>(null);
 | 
				
			||||||
    const [busy, setBusy] = useState<boolean>(false);
 | 
					    const [busy, setBusy] = useState<boolean>(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,42 +172,26 @@ const SpaceCreateMenu = ({ onFinished }) => {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const initialState: ICreateRoomStateEvent[] = [
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                type: EventType.RoomHistoryVisibility,
 | 
					 | 
				
			||||||
                content: {
 | 
					 | 
				
			||||||
                    "history_visibility": visibility === Visibility.Public ? "world_readable" : "invited",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
        if (avatar) {
 | 
					 | 
				
			||||||
            const url = await cli.uploadContent(avatar);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            initialState.push({
 | 
					 | 
				
			||||||
                type: EventType.RoomAvatar,
 | 
					 | 
				
			||||||
                content: { url },
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            await createRoom({
 | 
					            await createRoom({
 | 
				
			||||||
                createOpts: {
 | 
					                createOpts: {
 | 
				
			||||||
                    preset: visibility === Visibility.Public ? Preset.PublicChat : Preset.PrivateChat,
 | 
					                    preset: visibility === Visibility.Public ? Preset.PublicChat : Preset.PrivateChat,
 | 
				
			||||||
                    name,
 | 
					                    name,
 | 
				
			||||||
                    creation_content: {
 | 
					 | 
				
			||||||
                        [RoomCreateTypeField]: RoomType.Space,
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    initial_state: initialState,
 | 
					 | 
				
			||||||
                    power_level_content_override: {
 | 
					                    power_level_content_override: {
 | 
				
			||||||
                        // Only allow Admins to write to the timeline to prevent hidden sync spam
 | 
					                        // Only allow Admins to write to the timeline to prevent hidden sync spam
 | 
				
			||||||
                        events_default: 100,
 | 
					                        events_default: 100,
 | 
				
			||||||
                        ...Visibility.Public ? { invite: 0 } : {},
 | 
					                        ...visibility === Visibility.Public ? { invite: 0 } : {},
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    room_alias_name: visibility === Visibility.Public && alias
 | 
					                    room_alias_name: visibility === Visibility.Public && alias
 | 
				
			||||||
                        ? alias.substr(1, alias.indexOf(":") - 1)
 | 
					                        ? alias.substr(1, alias.indexOf(":") - 1)
 | 
				
			||||||
                        : undefined,
 | 
					                        : undefined,
 | 
				
			||||||
                    topic,
 | 
					                    topic,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                avatar,
 | 
				
			||||||
 | 
					                roomType: RoomType.Space,
 | 
				
			||||||
 | 
					                historyVisibility: visibility === Visibility.Public
 | 
				
			||||||
 | 
					                    ? HistoryVisibility.WorldReadable
 | 
				
			||||||
 | 
					                    : HistoryVisibility.Invited,
 | 
				
			||||||
                spinner: false,
 | 
					                spinner: false,
 | 
				
			||||||
                encryption: false,
 | 
					                encryption: false,
 | 
				
			||||||
                andView: true,
 | 
					                andView: true,
 | 
				
			||||||
@@ -171,7 +229,6 @@ const SpaceCreateMenu = ({ onFinished }) => {
 | 
				
			|||||||
            <SpaceFeedbackPrompt onClick={onFinished} />
 | 
					            <SpaceFeedbackPrompt onClick={onFinished} />
 | 
				
			||||||
        </React.Fragment>;
 | 
					        </React.Fragment>;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        const domain = cli.getDomain();
 | 
					 | 
				
			||||||
        body = <React.Fragment>
 | 
					        body = <React.Fragment>
 | 
				
			||||||
            <AccessibleTooltipButton
 | 
					            <AccessibleTooltipButton
 | 
				
			||||||
                className="mx_SpaceCreateMenu_back"
 | 
					                className="mx_SpaceCreateMenu_back"
 | 
				
			||||||
@@ -192,49 +249,20 @@ const SpaceCreateMenu = ({ onFinished }) => {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <form className="mx_SpaceBasicSettings" onSubmit={onSpaceCreateClick}>
 | 
					            <SpaceCreateForm
 | 
				
			||||||
                <SpaceAvatar setAvatar={setAvatar} avatarDisabled={busy} />
 | 
					                busy={busy}
 | 
				
			||||||
 | 
					                onSubmit={onSpaceCreateClick}
 | 
				
			||||||
                <Field
 | 
					                setAvatar={setAvatar}
 | 
				
			||||||
                    name="spaceName"
 | 
					                name={name}
 | 
				
			||||||
                    label={_t("Name")}
 | 
					                setName={setName}
 | 
				
			||||||
                    autoFocus={true}
 | 
					                nameFieldRef={spaceNameField}
 | 
				
			||||||
                    value={name}
 | 
					                topic={topic}
 | 
				
			||||||
                    onChange={ev => {
 | 
					                setTopic={setTopic}
 | 
				
			||||||
                        const newName = ev.target.value;
 | 
					                alias={alias}
 | 
				
			||||||
                        if (!alias || alias === nameToAlias(name, domain)) {
 | 
					                setAlias={setAlias}
 | 
				
			||||||
                            setAlias(nameToAlias(newName, domain));
 | 
					                showAliasField={visibility === Visibility.Public}
 | 
				
			||||||
                        }
 | 
					                aliasFieldRef={spaceAliasField}
 | 
				
			||||||
                        setName(newName);
 | 
					            />
 | 
				
			||||||
                    }}
 | 
					 | 
				
			||||||
                    ref={spaceNameField}
 | 
					 | 
				
			||||||
                    onValidate={spaceNameValidator}
 | 
					 | 
				
			||||||
                    disabled={busy}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                { visibility === Visibility.Public
 | 
					 | 
				
			||||||
                    ? <RoomAliasField
 | 
					 | 
				
			||||||
                        ref={spaceAliasField}
 | 
					 | 
				
			||||||
                        onChange={setAlias}
 | 
					 | 
				
			||||||
                        domain={domain}
 | 
					 | 
				
			||||||
                        value={alias}
 | 
					 | 
				
			||||||
                        placeholder={name ? nameToAlias(name, domain) : _t("e.g. my-space")}
 | 
					 | 
				
			||||||
                        label={_t("Address")}
 | 
					 | 
				
			||||||
                        disabled={busy}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                    : null
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <Field
 | 
					 | 
				
			||||||
                    name="spaceTopic"
 | 
					 | 
				
			||||||
                    element="textarea"
 | 
					 | 
				
			||||||
                    label={_t("Description")}
 | 
					 | 
				
			||||||
                    value={topic}
 | 
					 | 
				
			||||||
                    onChange={ev => setTopic(ev.target.value)}
 | 
					 | 
				
			||||||
                    rows={3}
 | 
					 | 
				
			||||||
                    disabled={busy}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
            </form>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={busy}>
 | 
					            <AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={busy}>
 | 
				
			||||||
                { busy ? _t("Creating...") : _t("Create") }
 | 
					                { busy ? _t("Creating...") : _t("Create") }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ import {
 | 
				
			|||||||
    shouldShowSpaceSettings,
 | 
					    shouldShowSpaceSettings,
 | 
				
			||||||
    showAddExistingRooms,
 | 
					    showAddExistingRooms,
 | 
				
			||||||
    showCreateNewRoom,
 | 
					    showCreateNewRoom,
 | 
				
			||||||
 | 
					    showCreateNewSubspace,
 | 
				
			||||||
    showSpaceInvite,
 | 
					    showSpaceInvite,
 | 
				
			||||||
    showSpaceSettings,
 | 
					    showSpaceSettings,
 | 
				
			||||||
} from "../../../utils/space";
 | 
					} from "../../../utils/space";
 | 
				
			||||||
@@ -48,6 +49,7 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
				
			|||||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
 | 
					import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
 | 
				
			||||||
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
 | 
					import { NotificationColor } from "../../../stores/notifications/NotificationColor";
 | 
				
			||||||
import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager";
 | 
					import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager";
 | 
				
			||||||
 | 
					import { BetaPill } from "../beta/BetaCard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IItemProps extends InputHTMLAttributes<HTMLLIElement> {
 | 
					interface IItemProps extends InputHTMLAttributes<HTMLLIElement> {
 | 
				
			||||||
    space?: Room;
 | 
					    space?: Room;
 | 
				
			||||||
@@ -234,6 +236,14 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
 | 
				
			|||||||
        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
					        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private onNewSubspaceClick = (ev: ButtonEvent) => {
 | 
				
			||||||
 | 
					        ev.preventDefault();
 | 
				
			||||||
 | 
					        ev.stopPropagation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        showCreateNewSubspace(this.props.space);
 | 
				
			||||||
 | 
					        this.setState({ contextMenuPosition: null }); // also close the menu
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private onMembersClick = (ev: ButtonEvent) => {
 | 
					    private onMembersClick = (ev: ButtonEvent) => {
 | 
				
			||||||
        ev.preventDefault();
 | 
					        ev.preventDefault();
 | 
				
			||||||
        ev.stopPropagation();
 | 
					        ev.stopPropagation();
 | 
				
			||||||
@@ -318,6 +328,13 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
 | 
				
			|||||||
                        label={_t("Add existing room")}
 | 
					                        label={_t("Add existing room")}
 | 
				
			||||||
                        onClick={this.onAddExistingRoomClick}
 | 
					                        onClick={this.onAddExistingRoomClick}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
 | 
					                    <IconizedContextMenuOption
 | 
				
			||||||
 | 
					                        iconClassName="mx_SpacePanel_iconPlus"
 | 
				
			||||||
 | 
					                        label={_t("Add subspace")}
 | 
				
			||||||
 | 
					                        onClick={this.onNewSubspaceClick}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        <BetaPill />
 | 
				
			||||||
 | 
					                    </IconizedContextMenuOption>
 | 
				
			||||||
                </IconizedContextMenuOptionList>;
 | 
					                </IconizedContextMenuOptionList>;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,9 +18,15 @@ limitations under the License.
 | 
				
			|||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
 | 
					import { MatrixClient } from "matrix-js-sdk/src/client";
 | 
				
			||||||
import { Room } from "matrix-js-sdk/src/models/room";
 | 
					import { Room } from "matrix-js-sdk/src/models/room";
 | 
				
			||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | 
					import { RoomMember } from "matrix-js-sdk/src/models/room-member";
 | 
				
			||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
 | 
					import { EventType, RoomCreateTypeField, RoomType } from "matrix-js-sdk/src/@types/event";
 | 
				
			||||||
import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
 | 
					import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests";
 | 
				
			||||||
import { JoinRule, Preset, RestrictedAllowType, Visibility } from "matrix-js-sdk/src/@types/partials";
 | 
					import {
 | 
				
			||||||
 | 
					    HistoryVisibility,
 | 
				
			||||||
 | 
					    JoinRule,
 | 
				
			||||||
 | 
					    Preset,
 | 
				
			||||||
 | 
					    RestrictedAllowType,
 | 
				
			||||||
 | 
					    Visibility,
 | 
				
			||||||
 | 
					} from "matrix-js-sdk/src/@types/partials";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { MatrixClientPeg } from './MatrixClientPeg';
 | 
					import { MatrixClientPeg } from './MatrixClientPeg';
 | 
				
			||||||
import Modal from './Modal';
 | 
					import Modal from './Modal';
 | 
				
			||||||
@@ -52,6 +58,9 @@ export interface IOpts {
 | 
				
			|||||||
    inlineErrors?: boolean;
 | 
					    inlineErrors?: boolean;
 | 
				
			||||||
    andView?: boolean;
 | 
					    andView?: boolean;
 | 
				
			||||||
    associatedWithCommunity?: string;
 | 
					    associatedWithCommunity?: string;
 | 
				
			||||||
 | 
					    avatar?: File | string; // will upload if given file, else mxcUrl is needed
 | 
				
			||||||
 | 
					    roomType?: RoomType | string;
 | 
				
			||||||
 | 
					    historyVisibility?: HistoryVisibility;
 | 
				
			||||||
    parentSpace?: Room;
 | 
					    parentSpace?: Room;
 | 
				
			||||||
    joinRule?: JoinRule;
 | 
					    joinRule?: JoinRule;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -112,6 +121,13 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
 | 
				
			|||||||
        createOpts.is_direct = true;
 | 
					        createOpts.is_direct = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opts.roomType) {
 | 
				
			||||||
 | 
					        createOpts.creation_content = {
 | 
				
			||||||
 | 
					            ...createOpts.creation_content,
 | 
				
			||||||
 | 
					            [RoomCreateTypeField]: opts.roomType,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // By default, view the room after creating it
 | 
					    // By default, view the room after creating it
 | 
				
			||||||
    if (opts.andView === undefined) {
 | 
					    if (opts.andView === undefined) {
 | 
				
			||||||
        opts.andView = true;
 | 
					        opts.andView = true;
 | 
				
			||||||
@@ -144,12 +160,11 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (opts.parentSpace) {
 | 
					    if (opts.parentSpace) {
 | 
				
			||||||
        createOpts.initial_state.push(makeSpaceParentEvent(opts.parentSpace, true));
 | 
					        createOpts.initial_state.push(makeSpaceParentEvent(opts.parentSpace, true));
 | 
				
			||||||
        createOpts.initial_state.push({
 | 
					        if (!opts.historyVisibility) {
 | 
				
			||||||
            type: EventType.RoomHistoryVisibility,
 | 
					            opts.historyVisibility = createOpts.preset === Preset.PublicChat
 | 
				
			||||||
            content: {
 | 
					                ? HistoryVisibility.WorldReadable
 | 
				
			||||||
                "history_visibility": createOpts.preset === Preset.PublicChat ? "world_readable" : "invited",
 | 
					                : HistoryVisibility.Invited;
 | 
				
			||||||
            },
 | 
					        }
 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (opts.joinRule === JoinRule.Restricted) {
 | 
					        if (opts.joinRule === JoinRule.Restricted) {
 | 
				
			||||||
            if (SpaceStore.instance.restrictedJoinRuleSupport?.preferred) {
 | 
					            if (SpaceStore.instance.restrictedJoinRuleSupport?.preferred) {
 | 
				
			||||||
@@ -176,6 +191,27 @@ export default async function createRoom(opts: IOpts): Promise<string | null> {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opts.avatar) {
 | 
				
			||||||
 | 
					        let url = opts.avatar;
 | 
				
			||||||
 | 
					        if (opts.avatar instanceof File) {
 | 
				
			||||||
 | 
					            url = await client.uploadContent(opts.avatar);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        createOpts.initial_state.push({
 | 
				
			||||||
 | 
					            type: EventType.RoomAvatar,
 | 
				
			||||||
 | 
					            content: { url },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opts.historyVisibility) {
 | 
				
			||||||
 | 
					        createOpts.initial_state.push({
 | 
				
			||||||
 | 
					            type: EventType.RoomHistoryVisibility,
 | 
				
			||||||
 | 
					            content: {
 | 
				
			||||||
 | 
					                "history_visibility": opts.historyVisibility,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let modal;
 | 
					    let modal;
 | 
				
			||||||
    if (opts.spinner) modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
 | 
					    if (opts.spinner) modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1008,6 +1008,8 @@
 | 
				
			|||||||
    "Name": "Name",
 | 
					    "Name": "Name",
 | 
				
			||||||
    "Description": "Description",
 | 
					    "Description": "Description",
 | 
				
			||||||
    "Please enter a name for the space": "Please enter a name for the space",
 | 
					    "Please enter a name for the space": "Please enter a name for the space",
 | 
				
			||||||
 | 
					    "e.g. my-space": "e.g. my-space",
 | 
				
			||||||
 | 
					    "Address": "Address",
 | 
				
			||||||
    "Create a space": "Create a space",
 | 
					    "Create a space": "Create a space",
 | 
				
			||||||
    "Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.": "Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.",
 | 
					    "Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.": "Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.",
 | 
				
			||||||
    "Public": "Public",
 | 
					    "Public": "Public",
 | 
				
			||||||
@@ -1020,8 +1022,6 @@
 | 
				
			|||||||
    "Your private space": "Your private space",
 | 
					    "Your private space": "Your private space",
 | 
				
			||||||
    "Add some details to help people recognise it.": "Add some details to help people recognise it.",
 | 
					    "Add some details to help people recognise it.": "Add some details to help people recognise it.",
 | 
				
			||||||
    "You can change these anytime.": "You can change these anytime.",
 | 
					    "You can change these anytime.": "You can change these anytime.",
 | 
				
			||||||
    "e.g. my-space": "e.g. my-space",
 | 
					 | 
				
			||||||
    "Address": "Address",
 | 
					 | 
				
			||||||
    "Creating...": "Creating...",
 | 
					    "Creating...": "Creating...",
 | 
				
			||||||
    "Create": "Create",
 | 
					    "Create": "Create",
 | 
				
			||||||
    "All rooms": "All rooms",
 | 
					    "All rooms": "All rooms",
 | 
				
			||||||
@@ -1059,6 +1059,7 @@
 | 
				
			|||||||
    "Leave space": "Leave space",
 | 
					    "Leave space": "Leave space",
 | 
				
			||||||
    "Create new room": "Create new room",
 | 
					    "Create new room": "Create new room",
 | 
				
			||||||
    "Add existing room": "Add existing room",
 | 
					    "Add existing room": "Add existing room",
 | 
				
			||||||
 | 
					    "Add subspace": "Add subspace",
 | 
				
			||||||
    "Members": "Members",
 | 
					    "Members": "Members",
 | 
				
			||||||
    "Manage & explore rooms": "Manage & explore rooms",
 | 
					    "Manage & explore rooms": "Manage & explore rooms",
 | 
				
			||||||
    "Explore rooms": "Explore rooms",
 | 
					    "Explore rooms": "Explore rooms",
 | 
				
			||||||
@@ -2108,17 +2109,20 @@
 | 
				
			|||||||
    "Add a new server...": "Add a new server...",
 | 
					    "Add a new server...": "Add a new server...",
 | 
				
			||||||
    "%(networkName)s rooms": "%(networkName)s rooms",
 | 
					    "%(networkName)s rooms": "%(networkName)s rooms",
 | 
				
			||||||
    "Matrix rooms": "Matrix rooms",
 | 
					    "Matrix rooms": "Matrix rooms",
 | 
				
			||||||
 | 
					    "Add existing space": "Add existing space",
 | 
				
			||||||
 | 
					    "Want to add a new space instead?": "Want to add a new space instead?",
 | 
				
			||||||
 | 
					    "Create a new subspace": "Create a new subspace",
 | 
				
			||||||
 | 
					    "Search for spaces": "Search for spaces",
 | 
				
			||||||
    "Not all selected were added": "Not all selected were added",
 | 
					    "Not all selected were added": "Not all selected were added",
 | 
				
			||||||
    "Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)",
 | 
					    "Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)",
 | 
				
			||||||
    "Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...",
 | 
					    "Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...",
 | 
				
			||||||
    "Filter your rooms and spaces": "Filter your rooms and spaces",
 | 
					 | 
				
			||||||
    "Feeling experimental?": "Feeling experimental?",
 | 
					 | 
				
			||||||
    "You can add existing spaces to a space.": "You can add existing spaces to a space.",
 | 
					 | 
				
			||||||
    "Direct Messages": "Direct Messages",
 | 
					    "Direct Messages": "Direct Messages",
 | 
				
			||||||
    "Space selection": "Space selection",
 | 
					    "Space selection": "Space selection",
 | 
				
			||||||
    "Add existing rooms": "Add existing rooms",
 | 
					    "Add existing rooms": "Add existing rooms",
 | 
				
			||||||
    "Want to add a new room instead?": "Want to add a new room instead?",
 | 
					    "Want to add a new room instead?": "Want to add a new room instead?",
 | 
				
			||||||
    "Create a new room": "Create a new room",
 | 
					    "Create a new room": "Create a new room",
 | 
				
			||||||
 | 
					    "Search for rooms": "Search for rooms",
 | 
				
			||||||
 | 
					    "Adding spaces has moved.": "Adding spaces has moved.",
 | 
				
			||||||
    "Matrix ID": "Matrix ID",
 | 
					    "Matrix ID": "Matrix ID",
 | 
				
			||||||
    "Matrix Room ID": "Matrix Room ID",
 | 
					    "Matrix Room ID": "Matrix Room ID",
 | 
				
			||||||
    "email address": "email address",
 | 
					    "email address": "email address",
 | 
				
			||||||
@@ -2205,13 +2209,20 @@
 | 
				
			|||||||
    "Create a room in %(communityName)s": "Create a room in %(communityName)s",
 | 
					    "Create a room in %(communityName)s": "Create a room in %(communityName)s",
 | 
				
			||||||
    "Create a public room": "Create a public room",
 | 
					    "Create a public room": "Create a public room",
 | 
				
			||||||
    "Create a private room": "Create a private room",
 | 
					    "Create a private room": "Create a private room",
 | 
				
			||||||
 | 
					    "Topic (optional)": "Topic (optional)",
 | 
				
			||||||
 | 
					    "Room visibility": "Room visibility",
 | 
				
			||||||
    "Private room (invite only)": "Private room (invite only)",
 | 
					    "Private room (invite only)": "Private room (invite only)",
 | 
				
			||||||
    "Public room": "Public room",
 | 
					    "Public room": "Public room",
 | 
				
			||||||
    "Visible to space members": "Visible to space members",
 | 
					    "Visible to space members": "Visible to space members",
 | 
				
			||||||
    "Topic (optional)": "Topic (optional)",
 | 
					 | 
				
			||||||
    "Room visibility": "Room visibility",
 | 
					 | 
				
			||||||
    "Block anyone not part of %(serverName)s from ever joining this room.": "Block anyone not part of %(serverName)s from ever joining this room.",
 | 
					    "Block anyone not part of %(serverName)s from ever joining this room.": "Block anyone not part of %(serverName)s from ever joining this room.",
 | 
				
			||||||
    "Create Room": "Create Room",
 | 
					    "Create Room": "Create Room",
 | 
				
			||||||
 | 
					    "Create a subspace": "Create a subspace",
 | 
				
			||||||
 | 
					    "Add a subspace to a space you manage.": "Add a subspace to a space you manage.",
 | 
				
			||||||
 | 
					    "Subspace visibility": "Subspace visibility",
 | 
				
			||||||
 | 
					    "Private subspace (invite only)": "Private subspace (invite only)",
 | 
				
			||||||
 | 
					    "Public subspace": "Public subspace",
 | 
				
			||||||
 | 
					    "Want to add an existing space instead?": "Want to add an existing space instead?",
 | 
				
			||||||
 | 
					    "Adding...": "Adding...",
 | 
				
			||||||
    "Sign out": "Sign out",
 | 
					    "Sign out": "Sign out",
 | 
				
			||||||
    "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this": "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this",
 | 
					    "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this": "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this",
 | 
				
			||||||
    "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.",
 | 
					    "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.": "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.",
 | 
				
			||||||
@@ -2813,6 +2824,7 @@
 | 
				
			|||||||
    "Creating rooms...": "Creating rooms...",
 | 
					    "Creating rooms...": "Creating rooms...",
 | 
				
			||||||
    "What do you want to organise?": "What do you want to organise?",
 | 
					    "What do you want to organise?": "What do you want to organise?",
 | 
				
			||||||
    "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.": "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
 | 
					    "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.": "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
 | 
				
			||||||
 | 
					    "Search for rooms or spaces": "Search for rooms or spaces",
 | 
				
			||||||
    "Share %(name)s": "Share %(name)s",
 | 
					    "Share %(name)s": "Share %(name)s",
 | 
				
			||||||
    "It's just you at the moment, it will be even better with others.": "It's just you at the moment, it will be even better with others.",
 | 
					    "It's just you at the moment, it will be even better with others.": "It's just you at the moment, it will be even better with others.",
 | 
				
			||||||
    "Go to my first room": "Go to my first room",
 | 
					    "Go to my first room": "Go to my first room",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,8 @@ import { _t } from "../languageHandler";
 | 
				
			|||||||
import SpacePublicShare from "../components/views/spaces/SpacePublicShare";
 | 
					import SpacePublicShare from "../components/views/spaces/SpacePublicShare";
 | 
				
			||||||
import InfoDialog from "../components/views/dialogs/InfoDialog";
 | 
					import InfoDialog from "../components/views/dialogs/InfoDialog";
 | 
				
			||||||
import { showRoomInviteDialog } from "../RoomInvite";
 | 
					import { showRoomInviteDialog } from "../RoomInvite";
 | 
				
			||||||
 | 
					import CreateSubspaceDialog from "../components/views/dialogs/CreateSubspaceDialog";
 | 
				
			||||||
 | 
					import AddExistingSubspaceDialog from "../components/views/dialogs/AddExistingSubspaceDialog";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const shouldShowSpaceSettings = (space: Room) => {
 | 
					export const shouldShowSpaceSettings = (space: Room) => {
 | 
				
			||||||
    const userId = space.client.getUserId();
 | 
					    const userId = space.client.getUserId();
 | 
				
			||||||
@@ -54,18 +56,18 @@ export const showSpaceSettings = (space: Room) => {
 | 
				
			|||||||
    }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
 | 
					    }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const showAddExistingRooms = async (space: Room) => {
 | 
					export const showAddExistingRooms = async (space: Room): Promise<[boolean]> => {
 | 
				
			||||||
    return Modal.createTrackedDialog(
 | 
					    return Modal.createTrackedDialog(
 | 
				
			||||||
        "Space Landing",
 | 
					        "Space Landing",
 | 
				
			||||||
        "Add Existing",
 | 
					        "Add Existing",
 | 
				
			||||||
        AddExistingToSpaceDialog,
 | 
					        AddExistingToSpaceDialog,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            matrixClient: space.client,
 | 
					            onCreateRoomClick: () => showCreateNewRoom(space),
 | 
				
			||||||
            onCreateRoomClick: showCreateNewRoom,
 | 
					            onAddSubspaceClick: () => showAddExistingSubspace(space),
 | 
				
			||||||
            space,
 | 
					            space,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "mx_AddExistingToSpaceDialog_wrapper",
 | 
					        "mx_AddExistingToSpaceDialog_wrapper",
 | 
				
			||||||
    ).finished;
 | 
					    ).finished as Promise<[boolean]>;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const showCreateNewRoom = async (space: Room) => {
 | 
					export const showCreateNewRoom = async (space: Room) => {
 | 
				
			||||||
@@ -102,3 +104,29 @@ export const showSpaceInvite = (space: Room, initialText = "") => {
 | 
				
			|||||||
        showRoomInviteDialog(space.roomId, initialText);
 | 
					        showRoomInviteDialog(space.roomId, initialText);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const showAddExistingSubspace = async (space: Room): Promise<[boolean]> => {
 | 
				
			||||||
 | 
					    return Modal.createTrackedDialog(
 | 
				
			||||||
 | 
					        "Space Landing",
 | 
				
			||||||
 | 
					        "Create Subspace",
 | 
				
			||||||
 | 
					        AddExistingSubspaceDialog,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            space,
 | 
				
			||||||
 | 
					            onCreateSubspaceClick: () => showCreateNewSubspace(space),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "mx_AddExistingToSpaceDialog_wrapper",
 | 
				
			||||||
 | 
					    ).finished as Promise<[boolean]>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const showCreateNewSubspace = async (space: Room): Promise<[boolean]> => {
 | 
				
			||||||
 | 
					    return Modal.createTrackedDialog(
 | 
				
			||||||
 | 
					        "Space Landing",
 | 
				
			||||||
 | 
					        "Create Subspace",
 | 
				
			||||||
 | 
					        CreateSubspaceDialog,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            space,
 | 
				
			||||||
 | 
					            onAddExistingSpaceClick: () => showAddExistingSubspace(space),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "mx_CreateSubspaceDialog_wrapper",
 | 
				
			||||||
 | 
					    ).finished as Promise<[boolean]>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user