diff --git a/res/css/_components.scss b/res/css/_components.scss
index 5145133127..60725ff5d4 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -72,6 +72,7 @@
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
@import "./views/dialogs/_NewSessionReviewDialog.scss";
+@import "./views/dialogs/_PrototypeCreateGroupDialog.scss";
@import "./views/dialogs/_RoomSettingsDialog.scss";
@import "./views/dialogs/_RoomSettingsDialogBridges.scss";
@import "./views/dialogs/_RoomUpgradeDialog.scss";
@@ -106,6 +107,7 @@
@import "./views/elements/_FormButton.scss";
@import "./views/elements/_IconButton.scss";
@import "./views/elements/_ImageView.scss";
+@import "./views/elements/_InfoTooltip.scss";
@import "./views/elements/_InlineSpinner.scss";
@import "./views/elements/_ManageIntegsButton.scss";
@import "./views/elements/_PowerSelector.scss";
diff --git a/res/css/views/dialogs/_PrototypeCreateGroupDialog.scss b/res/css/views/dialogs/_PrototypeCreateGroupDialog.scss
new file mode 100644
index 0000000000..a0f505b1ff
--- /dev/null
+++ b/res/css/views/dialogs/_PrototypeCreateGroupDialog.scss
@@ -0,0 +1,102 @@
+/*
+Copyright 2020 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_PrototypeCreateGroupDialog {
+ .mx_Dialog_content {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 12px;
+
+ .mx_PrototypeCreateGroupDialog_colName {
+ flex-basis: 66.66%;
+ padding-right: 100px;
+
+ .mx_Field input {
+ font-size: $font-16px;
+ line-height: $font-20px;
+ }
+
+ .mx_PrototypeCreateGroupDialog_subtext {
+ display: block;
+ color: $muted-fg-color;
+ margin-bottom: 16px;
+
+ &:last-child {
+ margin-top: 16px;
+ }
+
+ &.mx_PrototypeCreateGroupDialog_subtext_error {
+ color: $warning-color;
+ }
+ }
+
+ .mx_PrototypeCreateGroupDialog_communityId {
+ position: relative;
+
+ .mx_InfoTooltip {
+ float: right;
+ }
+ }
+
+ .mx_AccessibleButton {
+ display: block;
+ height: 32px;
+ font-size: $font-16px;
+ line-height: 32px;
+ }
+ }
+
+ .mx_PrototypeCreateGroupDialog_colAvatar {
+ flex-basis: 33.33%;
+
+ .mx_PrototypeCreateGroupDialog_avatarContainer {
+ margin-top: 12px;
+ margin-bottom: 20px;
+
+ .mx_PrototypeCreateGroupDialog_avatar,
+ .mx_PrototypeCreateGroupDialog_placeholderAvatar {
+ width: 96px;
+ height: 96px;
+ border-radius: 96px;
+ }
+
+ .mx_PrototypeCreateGroupDialog_placeholderAvatar {
+ background-color: #368BD6; // hardcoded for both themes
+
+ &::before {
+ display: inline-block;
+ background-color: #fff; // hardcoded because the background is
+ mask-repeat: no-repeat;
+ mask-size: 96px;
+ width: 96px;
+ height: 96px;
+ mask-position: center;
+ content: '';
+ vertical-align: middle;
+ mask-image: url('$(res)/img/element-icons/add-photo.svg');
+ }
+ }
+ }
+
+ .mx_PrototypeCreateGroupDialog_tip {
+ &>b, &>span {
+ display: block;
+ color: $muted-fg-color;
+ }
+ }
+ }
+ }
+}
diff --git a/res/css/views/elements/_InfoTooltip.scss b/res/css/views/elements/_InfoTooltip.scss
new file mode 100644
index 0000000000..5858a60629
--- /dev/null
+++ b/res/css/views/elements/_InfoTooltip.scss
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 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_InfoTooltip_icon {
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+}
+
+.mx_InfoTooltip_icon::before {
+ display: inline-block;
+ background-color: $muted-fg-color;
+ mask-repeat: no-repeat;
+ mask-size: 16px;
+ width: 16px;
+ height: 16px;
+ mask-position: center;
+ content: '';
+ vertical-align: middle;
+ mask-image: url('$(res)/img/element-icons/info.svg');
+}
diff --git a/res/img/element-icons/add-photo.svg b/res/img/element-icons/add-photo.svg
new file mode 100644
index 0000000000..bde5253bea
--- /dev/null
+++ b/res/img/element-icons/add-photo.svg
@@ -0,0 +1,5 @@
+
diff --git a/res/img/element-icons/info.svg b/res/img/element-icons/info.svg
new file mode 100644
index 0000000000..b5769074ab
--- /dev/null
+++ b/res/img/element-icons/info.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index a10af429b9..2b764d00c9 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -77,6 +77,7 @@ import ErrorDialog from "../views/dialogs/ErrorDialog";
import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
import { SettingLevel } from "../../settings/SettingLevel";
import { leaveRoomBehaviour } from "../../utils/membership";
+import PrototypeCreateGroupDialog from "../views/dialogs/PrototypeCreateGroupDialog";
/** constants for MatrixChat.state.view */
export enum Views {
@@ -620,7 +621,10 @@ export default class MatrixChat extends React.PureComponent {
this.createRoom(payload.public);
break;
case 'view_create_group': {
- const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog");
+ let CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog")
+ if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
+ CreateGroupDialog = PrototypeCreateGroupDialog;
+ }
Modal.createTrackedDialog('Create Community', '', CreateGroupDialog);
break;
}
diff --git a/src/components/views/dialogs/IDialogProps.ts b/src/components/views/dialogs/IDialogProps.ts
new file mode 100644
index 0000000000..1027ca7607
--- /dev/null
+++ b/src/components/views/dialogs/IDialogProps.ts
@@ -0,0 +1,19 @@
+/*
+Copyright 2020 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.
+*/
+
+export interface IDialogProps {
+ onFinished: (bool) => void;
+}
diff --git a/src/components/views/dialogs/PrototypeCreateGroupDialog.tsx b/src/components/views/dialogs/PrototypeCreateGroupDialog.tsx
new file mode 100644
index 0000000000..7427b2737c
--- /dev/null
+++ b/src/components/views/dialogs/PrototypeCreateGroupDialog.tsx
@@ -0,0 +1,217 @@
+/*
+Copyright 2020 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, { ChangeEvent } from 'react';
+import BaseDialog from "./BaseDialog";
+import { _t } from "../../../languageHandler";
+import { IDialogProps } from "./IDialogProps";
+import Field from "../elements/Field";
+import AccessibleButton from "../elements/AccessibleButton";
+import { MatrixClientPeg } from "../../../MatrixClientPeg";
+import InfoTooltip from "../elements/InfoTooltip";
+import dis from "../../../dispatcher/dispatcher";
+
+interface IProps extends IDialogProps {
+}
+
+interface IState {
+ name: string;
+ localpart: string;
+ error: string;
+ busy: boolean;
+ avatarFile: File;
+ avatarPreview: string;
+}
+
+export default class PrototypeCreateGroupDialog extends React.PureComponent {
+ private avatarUploadRef: React.RefObject = React.createRef();
+
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = {
+ name: "",
+ localpart: "",
+ error: null,
+ busy: false,
+ avatarFile: null,
+ avatarPreview: null,
+ };
+ }
+
+ private onNameChange = (ev: ChangeEvent) => {
+ const localpart = (ev.target.value || "").toLowerCase().replace(/[^a-z0-9.\-_]/g, '-');
+ this.setState({name: ev.target.value, localpart});
+ };
+
+ private onSubmit = async (ev) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ if (this.state.busy) return;
+
+ // We'll create the community now to see if it's taken, leaving it active in
+ // the background for the user to look at while they invite people.
+ this.setState({busy: true});
+ try {
+ let avatarUrl = null;
+ if (this.state.avatarFile) {
+ avatarUrl = await MatrixClientPeg.get().uploadContent(this.state.avatarFile);
+ }
+
+ const result = await MatrixClientPeg.get().createGroup({
+ localpart: this.state.localpart,
+ profile: {
+ name: this.state.name,
+ avatar_url: avatarUrl,
+ },
+ });
+
+ // Ensure the tag gets selected now that we've created it
+ dis.dispatch({action: 'deselect_tags'}, true);
+ dis.dispatch({
+ action: 'select_tag',
+ tag: result.group_id,
+ });
+
+ if (result.room_id) {
+ dis.dispatch({
+ action: 'view_room',
+ room_id: result.room_id,
+ });
+ } else {
+ dis.dispatch({
+ action: 'view_group',
+ group_id: result.group_id,
+ group_is_new: true,
+ });
+ }
+
+ // TODO: Show invite dialog
+ } catch (e) {
+ console.error(e);
+ this.setState({
+ busy: false,
+ error: _t(
+ "There was an error creating your community. The name may be taken or the " +
+ "server is unable to process your request.",
+ ),
+ });
+ }
+ };
+
+ private onAvatarChanged = (e: ChangeEvent) => {
+ if (!e.target.files || !e.target.files.length) {
+ this.setState({avatarFile: null});
+ } else {
+ this.setState({busy: true});
+ const file = e.target.files[0];
+ const reader = new FileReader();
+ reader.onload = (ev: ProgressEvent) => {
+ this.setState({avatarFile: file, busy: false, avatarPreview: ev.target.result as string});
+ };
+ reader.readAsDataURL(file);
+ }
+ };
+
+ private onChangeAvatar = () => {
+ if (this.avatarUploadRef.current) this.avatarUploadRef.current.click();
+ };
+
+ public render() {
+ let communityId = null;
+ if (this.state.localpart) {
+ communityId = (
+
+ {_t("Community ID: +:%(domain)s", {
+ domain: MatrixClientPeg.getHomeserverName(),
+ }, {
+ localpart: () => {this.state.localpart},
+ })}
+
+
+ );
+ }
+
+ let helpText = (
+
+ {_t("You can change this later if needed.")}
+
+ );
+ if (this.state.error) {
+ helpText = (
+
+ {this.state.error}
+
+ );
+ }
+
+ let preview = ;
+ if (!this.state.avatarPreview) {
+ preview =
+ }
+
+ return (
+
+
+
+ );
+ }
+}
diff --git a/src/components/views/dialogs/ServerOfflineDialog.tsx b/src/components/views/dialogs/ServerOfflineDialog.tsx
index f6767dcb8d..81f628343b 100644
--- a/src/components/views/dialogs/ServerOfflineDialog.tsx
+++ b/src/components/views/dialogs/ServerOfflineDialog.tsx
@@ -27,9 +27,9 @@ import Spinner from "../elements/Spinner";
import AccessibleButton from "../elements/AccessibleButton";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
+import { IDialogProps } from "./IDialogProps";
-interface IProps {
- onFinished: (bool) => void;
+interface IProps extends IDialogProps {
}
export default class ServerOfflineDialog extends React.PureComponent {
diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx
index dc2a987f13..22f83d391c 100644
--- a/src/components/views/dialogs/ShareDialog.tsx
+++ b/src/components/views/dialogs/ShareDialog.tsx
@@ -31,6 +31,7 @@ import {toRightOf} from "../../structures/ContextMenu";
import {copyPlaintext, selectText} from "../../../utils/strings";
import StyledCheckbox from '../elements/StyledCheckbox';
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
+import { IDialogProps } from "./IDialogProps";
const socials = [
{
@@ -60,8 +61,7 @@ const socials = [
},
];
-interface IProps {
- onFinished: () => void;
+interface IProps extends IDialogProps {
target: Room | User | Group | RoomMember | MatrixEvent;
permalinkCreator: RoomPermalinkCreator;
}
diff --git a/src/components/views/elements/InfoTooltip.tsx b/src/components/views/elements/InfoTooltip.tsx
new file mode 100644
index 0000000000..645951aab9
--- /dev/null
+++ b/src/components/views/elements/InfoTooltip.tsx
@@ -0,0 +1,73 @@
+/*
+Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
+Copyright 2019 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 classNames from 'classnames';
+
+import AccessibleButton from "./AccessibleButton";
+import Tooltip from './Tooltip';
+import { _t } from "../../../languageHandler";
+
+interface ITooltipProps {
+ tooltip?: React.ReactNode;
+ tooltipClassName?: string;
+}
+
+interface IState {
+ hover: boolean;
+}
+
+export default class InfoTooltip extends React.PureComponent {
+ constructor(props: ITooltipProps) {
+ super(props);
+ this.state = {
+ hover: false,
+ };
+ }
+
+ onMouseOver = () => {
+ this.setState({
+ hover: true,
+ });
+ };
+
+ onMouseLeave = () => {
+ this.setState({
+ hover: false,
+ });
+ };
+
+ render() {
+ const {tooltip, children, tooltipClassName} = this.props;
+ const title = _t("Information");
+
+ // Tooltip are forced on the right for a more natural feel to them on info icons
+ const tip = this.state.hover ? : ;
+ return (
+
+
+ {children}
+ {tip}
+
+ );
+ }
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index d052ba5a51..9a246a421d 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1484,6 +1484,7 @@
"Rotate Right": "Rotate Right",
"Rotate clockwise": "Rotate clockwise",
"Download this file": "Download this file",
+ "Information": "Information",
"Language Dropdown": "Language Dropdown",
"Manage Integrations": "Manage Integrations",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
@@ -1728,6 +1729,14 @@
"Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:",
"If you didn’t sign in to this session, your account may be compromised.": "If you didn’t sign in to this session, your account may be compromised.",
"This wasn't me": "This wasn't me",
+ "There was an error creating your community. The name may be taken or the server is unable to process your request.": "There was an error creating your community. The name may be taken or the server is unable to process your request.",
+ "Community ID: +:%(domain)s": "Community ID: +:%(domain)s",
+ "Use this when referencing your community to others. The community ID cannot be changed.": "Use this when referencing your community to others. The community ID cannot be changed.",
+ "You can change this later if needed.": "You can change this later if needed.",
+ "What's the name of your community or team?": "What's the name of your community or team?",
+ "Enter name": "Enter name",
+ "PRO TIP": "PRO TIP",
+ "An image will help people identify your community.": "An image will help people identify your community.",
"If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.",
"To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.": "To help avoid duplicate issues, please view existing issues first (and add a +1) or create a new issue if you can't find it.",
"Report bugs & give feedback": "Report bugs & give feedback",