Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(flat-components): users panel #1801

Merged
merged 3 commits into from
Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
});
Expand All @@ -36,8 +37,6 @@ Overview.args = {
unreadCount: faker.datatype.number(),
isCreator: faker.datatype.boolean(),
isBan: faker.datatype.boolean(),
hasHandRaising: faker.datatype.boolean(),
generateAvatar: () => "http://placekitten.com/64/64",
getUserByUUID: uuid => users.find(e => e.userUUID === uuid) || makeUser(),
messages: Array(20)
.fill(0)
Expand All @@ -49,9 +48,7 @@ Overview.args = {
text: chance.sentence({ words: faker.datatype.number(20) }),
senderID: chance.pickone(users).userUUID,
})),
ownerUUID: faker.datatype.uuid(),
userUUID: currentUser.userUUID,
users,
};
Overview.argTypes = {
loadMoreRows: { action: "loadMoreRows" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
display: flex;
align-items: center;
width: 100%;
height: 45px;
padding: 0 6px 2px;
border-top: 0.5px solid var(--grey-1);
height: 40px;
padding: 8px;
border-top: 1px solid var(--grey-1);
font-size: 0;
}

.chat-typebox-icon {
Expand Down Expand Up @@ -36,6 +37,8 @@
border: none;
outline: none;
color: var(--text);
font-size: 14px;
line-height: 24px;
background-color: transparent;

&::placeholder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
hasLeft: faker.datatype.boolean(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const ChatUser = /* @__PURE__ */ observer<ChatUserProps>(function ChatUse
/>
<span className="chat-user-name">{user.name}</span>
{ownerUUID === user.userUUID ? (
<span className="chat-user-status is-teacher">{t("teacher")}</span>
<span className="chat-user-status is-teacher">({t("teacher")})</span>
) : user.hasLeft ? (
<>
<span className="chat-user-status has-left">{t("has-left")}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
});
Expand Down
52 changes: 8 additions & 44 deletions packages/flat-components/src/components/ChatPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,24 @@
import "./style.less";

import React, { useMemo, useState } from "react";
import { Tabs } from "antd";
import React from "react";
import { observer } from "mobx-react-lite";
import { useTranslate } from "@netless/flat-i18n";
import { ChatMessages, ChatMessagesProps } from "./ChatMessages";
import { ChatTabTitle, ChatTabTitleProps } from "./ChatTabTitle";
import { ChatUsers, ChatUsersProps } from "./ChatUsers";

export type ChatPanelProps = ChatTabTitleProps &
Omit<ChatMessagesProps, "visible"> &
ChatUsersProps;
export type ChatPanelProps = ChatTabTitleProps & Omit<ChatMessagesProps, "visible">;

export const ChatPanel = /* @__PURE__ */ observer<ChatPanelProps>(function ChatPanel(props) {
const t = useTranslate();
const [activeTab, setActiveTab] = useState<"messages" | "users">("messages");
const usersCount = useMemo(() => {
const count = props.users.length;
if (count === 0) {
return "";
}
if (count > 999) {
return "(999+)";
}
return `(${count})`;
}, [props.users.length]);

return (
<div className="chat-panel">
<Tabs
activeKey={activeTab}
items={[
{
key: "messages",
label: (
<ChatTabTitle>
<span>{t("messages")}</span>
</ChatTabTitle>
),
children: <ChatMessages {...props} visible={activeTab === "messages"} />,
},
{
key: "users",
label: (
<ChatTabTitle {...props}>
<span>
{t("users")} {usersCount}
</span>
</ChatTabTitle>
),
children: <ChatUsers {...props} />,
},
]}
tabBarGutter={0}
onChange={setActiveTab as (key: string) => void}
></Tabs>
<div className="chat-panel-header">
<ChatTabTitle>
<span>{t("messages")}</span>
</ChatTabTitle>
</div>
<ChatMessages {...props} visible />
</div>
);
});
Expand Down
19 changes: 19 additions & 0 deletions packages/flat-components/src/components/ChatPanel/style.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.chat-panel {
height: 100%;
display: flex;
flex-flow: column nowrap;

.ant-tabs {
height: 100%;
Expand Down Expand Up @@ -43,6 +45,20 @@
}
}

.chat-panel-header {
flex-shrink: 0;
flex-grow: 0;
display: flex;
align-items: center;
height: 40px;
padding: 8px 12px;
font-size: 14px;
line-height: 24px;
font-weight: 600;
color: var(--text-strong);
border-bottom: 1px solid var(--grey-1);
}

.flat-color-scheme-dark {
.chat-panel {
.ant-tabs-ink-bar {
Expand All @@ -52,4 +68,7 @@
border-bottom-color: var(--grey-8);
}
}
.chat-panel-header {
border-bottom-color: var(--grey-8);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import "./style.less";

import React from "react";
import React, { useMemo } from "react";
import classNames from "classnames";
import { isInteger } from "lodash-es";
import { useTranslate } from "@netless/flat-i18n";
import { SVGHandUp } from "../../FlatIcons";

Expand All @@ -23,3 +25,33 @@ export const RaiseHand: React.FC<RaiseHandProps> = ({
</button>
);
};

export interface RaisingHandProps {
count: number;
onClick: () => void;
}

export const RaisingHand: React.FC<RaisingHandProps> = ({ count, onClick }) => {
const t = useTranslate();

const countLabel = useMemo(
() =>
isInteger(count) ? (
<span
className={classNames("raise-hand-red-dot", {
"is-large": count > 9,
})}
>
{count < 10 ? count : "9+"}
</span>
) : null,
[count],
);

return count > 0 ? (
<button className="raise-hand-btn" title={t("raise-your-hand")} onClick={onClick}>
<SVGHandUp active />
{countLabel}
</button>
) : null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,41 @@
border-radius: 24px;
border: 1px solid rgba(0, 0, 0, 0.15);
background: rgba(255, 255, 255, 0.9);
font-size: 0;
outline: 0;
}

.raise-hand-red-dot {
display: inline-flex;
justify-content: center;
align-items: center;
width: 22px;
height: 22px;
overflow: hidden;
position: absolute;
top: 0;
left: 75%;
transform: translate(-5px, -6px);
font-size: 12px;
border: 1px solid var(--grey-0);
border-radius: 22px;
color: var(--grey-0);
background: var(--danger);

&.is-large {
width: auto;
height: auto;
padding: 0 5px;
}
}

.flat-color-scheme-dark {
.raise-hand-btn {
background: var(--grey-10);
border: 1px solid rgba(255, 255, 255, 0.15);
background: var(--grey-9);
border: 1px solid var(--grey-8);
}

.raise-hand-red-dot {
border-color: var(--grey-11);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const IconMic = /* @__PURE__ */ React.memo<IconMicProps>(function IconMic
fill="#44AD00"
height={vHeight * 2}
style={{
transform: `translateY(${Math.pow(1 - volumeLevel, 2.3) * vHeight}px)`,
transform: `translateY(${(1 - volumeLevel) * vHeight}px)`,
transition: "transform .1s",
}}
width={vWidth}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { UsersPanel, UsersPanelProps } from "./index";
import { User } from "../../types/user";
import faker from "faker";
import Chance from "chance";

const storyMeta: Meta = {
title: "ChatPanel/UsersPanel",
component: UsersPanel,
};

export default storyMeta;

const chance = new Chance();
const makeUser = (): User => ({
userUUID: faker.datatype.uuid(),
name: faker.name.lastName(),
isSpeak: faker.datatype.boolean(),
wbOperate: faker.datatype.boolean(),
isRaiseHand: faker.datatype.boolean(),
avatar: "http://placekitten.com/64/64",
});
const currentUser = makeUser();
const users = (() => {
const users = Array(20).fill(0).map(makeUser);
users.push(currentUser);
return chance.shuffle(users);
})();

export const Overview: Story<UsersPanelProps> = args => (
<div style={{ width: "80vw", height: "400px", overflow: "hidden", border: "1px solid green" }}>
<UsersPanel {...args} />
</div>
);
const isCreator = chance.bool();
Overview.args = {
ownerUUID: isCreator ? currentUser.userUUID : chance.pickone(users).userUUID,
userUUID: currentUser.userUUID,
users,
getDeviceState: () => ({
camera: faker.datatype.boolean(),
mic: faker.datatype.boolean(),
}),
getVolumeLevel: () => faker.datatype.number({ min: 0, max: 1, precision: 0.01 }),
};
Loading