diff --git a/src/Avatar.jsx b/src/Avatar.jsx
new file mode 100644
index 0000000..85b6255
--- /dev/null
+++ b/src/Avatar.jsx
@@ -0,0 +1,58 @@
+import React, { useMemo } from "react";
+import classNames from "classnames";
+import styles from "./Avatar.module.css";
+
+const backgroundColors = [
+ "#5C56F5",
+ "#03B381",
+ "#368BD6",
+ "#AC3BA8",
+ "#E64F7A",
+ "#FF812D",
+ "#2DC2C5",
+ "#74D12C",
+];
+
+function hashStringToArrIndex(str, arrLength) {
+ let sum = 0;
+
+ for (let i = 0; i < str.length; i++) {
+ sum += str.charCodeAt(i);
+ }
+
+ return sum % arrLength;
+}
+
+export function Avatar({
+ bgKey,
+ src,
+ fallback,
+ size,
+ className,
+ style,
+ ...rest
+}) {
+ const backgroundColor = useMemo(() => {
+ const index = hashStringToArrIndex(
+ bgKey || fallback || src,
+ backgroundColors.length
+ );
+ return backgroundColors[index];
+ }, [bgKey, src, fallback]);
+
+ return (
+
+ {src ? (
+
+ ) : typeof fallback === "string" ? (
+
{fallback}
+ ) : (
+ fallback
+ )}
+
+ );
+}
diff --git a/src/Avatar.module.css b/src/Avatar.module.css
new file mode 100644
index 0000000..0fe6ff5
--- /dev/null
+++ b/src/Avatar.module.css
@@ -0,0 +1,46 @@
+.avatar {
+ position: relative;
+ color: #ffffff;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ font-weight: 600;
+}
+
+.avatar img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.avatar svg * {
+ fill: #ffffff;
+}
+
+.sm {
+ width: 22px;
+ height: 22px;
+ border-radius: 22px;
+ font-size: 14px;
+}
+
+.md {
+ width: 36px;
+ height: 36px;
+ border-radius: 36px;
+ font-size: 20px;
+}
+
+.lg {
+ width: 42px;
+ height: 42px;
+ border-radius: 42px;
+ font-size: 36px;
+}
+
+.xl {
+ width: 90px;
+ height: 90px;
+ border-radius: 90px;
+}
diff --git a/src/CallTile.jsx b/src/CallTile.jsx
new file mode 100644
index 0000000..94b7e42
--- /dev/null
+++ b/src/CallTile.jsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import { CopyButton } from "./button";
+import { Facepile } from "./Facepile";
+import { Avatar } from "./Avatar";
+import { ReactComponent as VideoIcon } from "./icons/Video.svg";
+import styles from "./CallTile.module.css";
+
+export function CallTile({ name, avatarUrl, roomUrl, participants }) {
+ return (
+
+ }
+ className={styles.avatar}
+ />
+
+
{name}
+
{roomUrl}
+ {participants &&
}
+
+
+
+ );
+}
diff --git a/src/CallTile.module.css b/src/CallTile.module.css
new file mode 100644
index 0000000..6902977
--- /dev/null
+++ b/src/CallTile.module.css
@@ -0,0 +1,54 @@
+.callTile {
+ display: flex;
+ width: 329px;
+ height: 94px;
+ padding: 12px;
+ text-decoration: none;
+ background-color: var(--bgColor2);
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.avatar,
+.copyButton {
+ flex-shrink: 0;
+}
+
+.callInfo {
+ display: flex;
+ flex-direction: column;
+ flex: 1;
+ padding: 0 16px;
+ color: var(--textColor1);
+ min-width: 0;
+}
+
+.callInfo > * {
+ margin-top: 0;
+ margin-bottom: 8px;
+}
+
+.callInfo > :last-child {
+ margin-bottom: 0;
+}
+
+.callInfo h5 {
+ font-size: 15px;
+ font-weight: 600;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.callInfo p {
+ font-weight: 400;
+ font-size: 12px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.copyButton {
+ width: 16px;
+ height: 16px;
+}
diff --git a/src/Facepile.jsx b/src/Facepile.jsx
index ae97161..3071d8d 100644
--- a/src/Facepile.jsx
+++ b/src/Facepile.jsx
@@ -1,32 +1,32 @@
import React from "react";
import styles from "./Facepile.module.css";
-import ColorHash from "color-hash";
import classNames from "classnames";
+import { Avatar } from "./Avatar";
-const colorHash = new ColorHash({ lightness: 0.3 });
-
-export function Facepile({ participants }) {
+export function Facepile({ className, participants, ...rest }) {
return (
member.name).join(", ")}
+ {...rest}
>
- {participants.slice(0, 3).map((member) => (
-
(
+
- {member.name.slice(0, 1).toUpperCase()}
-
+ style={{ left: i * 22 }}
+ />
))}
{participants.length > 3 && (
-
- {`+${participants.length - 3}`}
-
+ size="sm"
+ fallback={`+${participants.length - 3}`}
+ className={styles.avatar}
+ style={{ left: 3 * 22 }}
+ />
)}
);
diff --git a/src/Facepile.module.css b/src/Facepile.module.css
index 9654149..4add90e 100644
--- a/src/Facepile.module.css
+++ b/src/Facepile.module.css
@@ -1,31 +1,11 @@
.facepile {
- display: flex;
- margin: 0 16px;
+ width: 100%;
+ height: 24px;
+ position: relative;
}
.facepile .avatar {
- position: relative;
- width: 20px;
- height: 20px;
- border-radius: 20px;
- background-color: var(--primaryColor);
-}
-
-.facepile .avatar > * {
position: absolute;
- left: 0;
- color: #fff;
- text-align: center;
- pointer-events: none;
- font-weight: 600;
-}
-
-.facepile .avatar span {
- font-size: 14px;
- width: 20px;
- line-height: 20px;
-}
-
-.facepile .avatar.additional span {
- font-size: 12px;
+ top: 0;
+ border: 1px solid var(--bgColor2);
}
diff --git a/src/Home.jsx b/src/Home.jsx
index 91439cf..282d634 100644
--- a/src/Home.jsx
+++ b/src/Home.jsx
@@ -15,25 +15,21 @@ limitations under the License.
*/
import React, { useCallback, useState } from "react";
-import { useHistory, Link } from "react-router-dom";
+import { useHistory } from "react-router-dom";
import {
useGroupCallRooms,
usePublicRooms,
} from "./ConferenceCallManagerHooks";
import { Header, HeaderLogo, LeftNav, RightNav } from "./Header";
-import ColorHash from "color-hash";
import styles from "./Home.module.css";
import { FieldRow, InputField, ErrorMessage } from "./Input";
-import { Center, Content, Modal } from "./Layout";
import {
GroupCallIntent,
GroupCallType,
} from "matrix-js-sdk/src/browser-index";
-import { Facepile } from "./Facepile";
import { UserMenu } from "./UserMenu";
import { Button } from "./button";
-
-const colorHash = new ColorHash({ lightness: 0.3 });
+import { CallTile } from "./CallTile";
function roomAliasFromRoomName(roomName) {
return roomName
@@ -46,10 +42,8 @@ function roomAliasFromRoomName(roomName) {
export function Home({ client, onLogout }) {
const history = useHistory();
const [roomName, setRoomName] = useState("");
- const [roomAlias, setRoomAlias] = useState("");
const [guestAccess, setGuestAccess] = useState(false);
const [createRoomError, setCreateRoomError] = useState();
- const [showAdvanced, setShowAdvanced] = useState();
const rooms = useGroupCallRooms(client);
const publicRooms = usePublicRooms(
client,
@@ -110,131 +104,142 @@ export function Home({ client, onLogout }) {
const data = new FormData(e.target);
const roomName = data.get("roomName");
- const roomAlias = data.get("roomAlias");
const guestAccess = data.get("guestAccess");
- createRoom(roomName, roomAlias, guestAccess).catch((error) => {
- setCreateRoomError(error);
- setShowAdvanced(true);
- });
+ createRoom(roomName, roomAliasFromRoomName(roomName), guestAccess).catch(
+ (error) => {
+ setCreateRoomError(error);
+ setShowAdvanced(true);
+ }
+ );
},
[client]
);
+ const [roomId, setRoomId] = useState("");
+
+ const onJoinRoom = useCallback(
+ (e) => {
+ e.preventDefault();
+ const data = new FormData(e.target);
+ const roomId = data.get("roomId");
+ history.push(`/room/${roomId}`);
+ },
+ [history]
+ );
+
return (
- <>
-
-
-
-
-