@webinex/chatify 1.0.0-alpha00 → 1.0.0-alpha02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/ChatifyContext.d.ts +1 -1
  4. package/dist/cjs/types/ui/AddChatButton.d.ts +2 -0
  5. package/dist/cjs/types/ui/Avatar.d.ts +6 -0
  6. package/dist/cjs/types/ui/ChatHeaderMembers.d.ts +5 -0
  7. package/dist/cjs/types/ui/ChatHeaderSettingsButton.d.ts +5 -0
  8. package/dist/cjs/types/ui/Chatify.d.ts +17 -2
  9. package/dist/cjs/types/ui/Footer.d.ts +2 -0
  10. package/dist/cjs/types/ui/Header.d.ts +2 -0
  11. package/dist/cjs/types/ui/index.d.ts +5 -0
  12. package/dist/cjs/types/util/customize.d.ts +12 -0
  13. package/dist/cjs/types/util/index.d.ts +1 -0
  14. package/dist/css/chatify.css +3 -3
  15. package/dist/esm/index.js +1 -1
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/types/ChatifyContext.d.ts +1 -1
  18. package/dist/esm/types/ui/AddChatButton.d.ts +2 -0
  19. package/dist/esm/types/ui/Avatar.d.ts +6 -0
  20. package/dist/esm/types/ui/ChatHeaderMembers.d.ts +5 -0
  21. package/dist/esm/types/ui/ChatHeaderSettingsButton.d.ts +5 -0
  22. package/dist/esm/types/ui/Chatify.d.ts +17 -2
  23. package/dist/esm/types/ui/Footer.d.ts +2 -0
  24. package/dist/esm/types/ui/Header.d.ts +2 -0
  25. package/dist/esm/types/ui/index.d.ts +5 -0
  26. package/dist/esm/types/util/customize.d.ts +12 -0
  27. package/dist/esm/types/util/index.d.ts +1 -0
  28. package/dist/index.d.ts +48 -8
  29. package/package.json +1 -1
  30. package/src/ChatifyContext.tsx +5 -2
  31. package/src/ui/AddChatButton.tsx +19 -0
  32. package/src/ui/Aside.tsx +3 -2
  33. package/src/ui/Avatar.tsx +18 -0
  34. package/src/ui/ChatHeader.tsx +6 -22
  35. package/src/ui/ChatHeaderMembers.tsx +30 -0
  36. package/src/ui/ChatHeaderSettingsButton.tsx +26 -0
  37. package/src/ui/Chatify.tsx +30 -16
  38. package/src/ui/Footer.tsx +7 -0
  39. package/src/ui/Header.tsx +14 -0
  40. package/src/ui/MessageMd.tsx +2 -2
  41. package/src/ui/index.ts +5 -0
  42. package/src/ui/styles/index.scss +2 -2
  43. package/src/util/customize.tsx +45 -0
  44. package/src/util/index.ts +1 -0
package/src/ui/Aside.tsx CHANGED
@@ -1,9 +1,10 @@
1
- import { Avatar, Menu, MenuProps } from 'antd';
1
+ import { Menu, MenuProps } from 'antd';
2
2
  import { useCallback, useEffect, useMemo } from 'react';
3
3
  import type { MenuItemType } from 'antd/es/menu/hooks/useItems';
4
4
  import { useChatList, useDispatch, useSelect, ChatListItem } from '../core';
5
5
  import { useLocalizer } from './localizer';
6
6
  import { formatSystemMessage } from './SystemMessage';
7
+ import { Avatar } from './Avatar';
7
8
 
8
9
  function Chat(props: { chat: ChatListItem }) {
9
10
  const { chat } = props;
@@ -17,7 +18,7 @@ function Chat(props: { chat: ChatListItem }) {
17
18
  <div className="wxchtf-chat-last">
18
19
  {message.sentBy.id !== 'chatify::system' && (
19
20
  <div className="wxchtf-chat-last-author">
20
- <Avatar src={message.sentBy.avatar} /> {message.sentBy.name}
21
+ <Avatar account={message.sentBy} /> {message.sentBy.name}
21
22
  </div>
22
23
  )}
23
24
  <div className="wxchtf-chat-last-message">
@@ -0,0 +1,18 @@
1
+ import { FC } from 'react';
2
+ import { Account } from '../core';
3
+ import { Avatar as AntdAvatar } from 'antd';
4
+ import { customize } from '../util';
5
+
6
+ export interface AvatarProps {
7
+ account: Account;
8
+ }
9
+
10
+ const _Avatar: FC<AvatarProps> = (props: AvatarProps) => {
11
+ const { account } = props;
12
+
13
+ return <AntdAvatar src={account.avatar} />;
14
+ };
15
+
16
+ _Avatar.displayName = 'Avatar';
17
+
18
+ export const Avatar = customize('Avatar', _Avatar);
@@ -1,7 +1,7 @@
1
- import { useChat, useSelect, ChatListItem, useDispatch } from '../core';
2
- import { Avatar, Button, Col, Row, Space, Tooltip } from 'antd';
3
- import { SettingOutlined } from '@ant-design/icons';
4
- import { useCallback } from 'react';
1
+ import { useChat, useSelect, ChatListItem } from '../core';
2
+ import { Col, Row, Space } from 'antd';
3
+ import { ChatHeaderMembers } from './ChatHeaderMembers';
4
+ import { ChatSettingsButton } from './ChatHeaderSettingsButton';
5
5
 
6
6
  export interface ChatHeaderProps {
7
7
  id: string;
@@ -15,30 +15,14 @@ export function ChatHeader(props: ChatHeaderProps) {
15
15
  [id],
16
16
  );
17
17
 
18
- const dispatch = useDispatch();
19
- const onSettingsClick = useCallback(
20
- () => dispatch({ type: 'chat_settings_open', data: { chatId: id } }),
21
-
22
- // eslint-disable-next-line react-hooks/exhaustive-deps
23
- [dispatch],
24
- );
25
-
26
18
  return (
27
19
  <div className="wxchtf-chat-header">
28
20
  <Row justify="space-between" align="middle">
29
21
  <Col className="wxchtf-chat-name">{chat?.name ?? chatListItem?.name}</Col>
30
22
  <Col className="wxchtf-chat-members">
31
23
  <Space align="center">
32
- {chat?.members && (
33
- <Avatar.Group maxCount={5}>
34
- {chat.members.map((x) => (
35
- <Tooltip key={x.id} title={x.name}>
36
- <Avatar src={x.avatar}>{x.name}</Avatar>
37
- </Tooltip>
38
- ))}
39
- </Avatar.Group>
40
- )}
41
- <Button onClick={onSettingsClick} type="link" icon={<SettingOutlined />} />
24
+ <ChatHeaderMembers id={id} />
25
+ <ChatSettingsButton id={id} />
42
26
  </Space>
43
27
  </Col>
44
28
  </Row>
@@ -0,0 +1,30 @@
1
+ import { FC } from 'react';
2
+ import { useChat } from '../core';
3
+ import { Avatar, Tooltip } from 'antd';
4
+ import { customize } from '../util';
5
+
6
+ export interface ChatHeaderMembersProps {
7
+ id: string;
8
+ }
9
+
10
+ const _ChatHeaderMembers: FC<ChatHeaderMembersProps> = (props) => {
11
+ const { id } = props;
12
+ const { data: chat } = useChat({ chatId: id });
13
+
14
+ if (!chat?.members) {
15
+ return null;
16
+ }
17
+
18
+ return (
19
+ <Avatar.Group maxCount={5}>
20
+ {chat.members.map((x) => (
21
+ <Tooltip key={x.id} title={x.name}>
22
+ <Avatar src={x.avatar}>{x.name}</Avatar>
23
+ </Tooltip>
24
+ ))}
25
+ </Avatar.Group>
26
+ );
27
+ };
28
+
29
+ _ChatHeaderMembers.displayName = 'ChatHeaderMembers';
30
+ export const ChatHeaderMembers = customize('ChatHeaderMembers', _ChatHeaderMembers);
@@ -0,0 +1,26 @@
1
+ import { useDispatch } from '../core';
2
+ import { Button } from 'antd';
3
+ import { SettingOutlined } from '@ant-design/icons';
4
+ import { FC, useCallback } from 'react';
5
+ import { customize } from '../util';
6
+
7
+ export interface ChatSettingsButtonProps {
8
+ id: string;
9
+ }
10
+
11
+ const _ChatSettingsButton: FC<ChatSettingsButtonProps> = (props) => {
12
+ const { id } = props;
13
+ const dispatch = useDispatch();
14
+ const onSettingsClick = useCallback(
15
+ () => dispatch({ type: 'chat_settings_open', data: { chatId: id } }),
16
+
17
+ // eslint-disable-next-line react-hooks/exhaustive-deps
18
+ [dispatch],
19
+ );
20
+
21
+ return <Button onClick={onSettingsClick} type="link" icon={<SettingOutlined />} />;
22
+ };
23
+
24
+ _ChatSettingsButton.displayName = 'ChatSettingsButton';
25
+
26
+ export const ChatSettingsButton = customize('ChatSettingsButton', _ChatSettingsButton);
@@ -1,47 +1,61 @@
1
- import { CSSProperties, useCallback } from 'react';
2
- import { useSignalR, ReducerProvider, useDispatch, useReadMonitor } from '../core';
1
+ import { CSSProperties, FC } from 'react';
2
+ import { useSignalR, useReadMonitor } from '../core';
3
3
  import { Aside } from './Aside';
4
4
  import { Main } from './Main';
5
- import { Button } from 'antd';
6
5
  import { Localizer, LocalizerContext, defaultLocalizer } from './localizer';
6
+ import { Avatar } from './Avatar';
7
+ import { CustomizeProvider } from '../util';
8
+ import { ChatHeaderMembers } from './ChatHeaderMembers';
9
+ import { ChatSettingsButton } from './ChatHeaderSettingsButton';
10
+ import { Header } from './Header';
11
+ import { AddChatButton } from './AddChatButton';
12
+ import { Footer } from './Footer';
13
+
14
+ export interface ChatifyCustomizeValue {
15
+ Avatar?: typeof Avatar.Component | null;
16
+ Header?: typeof Header.Component | null;
17
+ Footer?: typeof Footer.Component | null;
18
+ AddChatButton?: typeof AddChatButton.Component | null;
19
+ ChatHeaderMembers?: typeof ChatHeaderMembers.Component | null;
20
+ ChatSettingsButton?: typeof ChatSettingsButton.Component | null;
21
+ }
7
22
 
8
23
  export interface ChatifyProps {
9
24
  className?: string;
10
25
  style?: CSSProperties;
11
26
  localizer?: Localizer;
27
+ customize?: ChatifyCustomizeValue;
12
28
  }
13
29
 
14
30
  function Content(props: Pick<ChatifyProps, 'className' | 'style'>) {
15
31
  const { className = '', style } = props;
16
32
  useSignalR();
17
33
  useReadMonitor();
18
- const dispatch = useDispatch();
19
- const onAddChat = useCallback(() => dispatch({ type: 'new_chat_open', data: undefined }), [dispatch]);
20
34
 
21
35
  return (
22
36
  <div className={className + ' wxchtf-chatify'} style={style}>
23
- <div className="wxchtf-header">
24
- <Button onClick={onAddChat} icon={'+'} type="link">
25
- Add chat
26
- </Button>
27
- </div>
37
+ <Header />
28
38
  <div className="wxchtf-body">
29
39
  <Aside />
30
40
  <Main />
31
41
  </div>
32
- <div className="wxchtf-footer"></div>
42
+ <Footer />
33
43
  </div>
34
44
  );
35
45
  }
36
46
 
37
- export function Chatify(props: ChatifyProps) {
38
- const { localizer = defaultLocalizer } = props;
47
+ const EMPTY_CUSTOMIZE = {};
48
+
49
+ export const Chatify: FC<ChatifyProps> = (props) => {
50
+ const { localizer = defaultLocalizer, customize = EMPTY_CUSTOMIZE } = props;
39
51
 
40
52
  return (
41
53
  <LocalizerContext.Provider value={localizer}>
42
- <ReducerProvider>
54
+ <CustomizeProvider value={customize}>
43
55
  <Content {...props} />
44
- </ReducerProvider>
56
+ </CustomizeProvider>
45
57
  </LocalizerContext.Provider>
46
58
  );
47
- }
59
+ };
60
+
61
+ Chatify.displayName = 'Chatify';
@@ -0,0 +1,7 @@
1
+ import { FC } from 'react';
2
+ import { customize } from '../util';
3
+
4
+ const _Footer: FC = () => <div className="wxchtf-footer"></div>;
5
+ _Footer.displayName = 'Footer';
6
+
7
+ export const Footer = customize('Footer', _Footer);
@@ -0,0 +1,14 @@
1
+ import { FC } from 'react';
2
+ import { AddChatButton } from './AddChatButton';
3
+ import { customize } from '../util';
4
+
5
+ const _Header: FC = () => {
6
+ return (
7
+ <div className="wxchtf-header">
8
+ <AddChatButton />
9
+ </div>
10
+ );
11
+ };
12
+
13
+ _Header.displayName = 'Header';
14
+ export const Header = customize('Header', _Header);
@@ -1,5 +1,5 @@
1
- import { Avatar } from 'antd';
2
1
  import { Account, File } from '../core';
2
+ import { Avatar } from './Avatar';
3
3
  import { useLocalizer } from './localizer';
4
4
  import React from 'react';
5
5
 
@@ -26,7 +26,7 @@ export const MessageMd = React.forwardRef(
26
26
  >
27
27
  {author && (
28
28
  <div className="wxchtf-message-author">
29
- <Avatar src={author.avatar} />
29
+ <Avatar account={author} />
30
30
  </div>
31
31
  )}
32
32
  <div className="wxchtf-message-body">
package/src/ui/index.ts CHANGED
@@ -1,2 +1,7 @@
1
1
  export * from './Chatify';
2
+ export * from './Avatar';
2
3
  export * from './localizer';
4
+ export * from './AddChatButton';
5
+ export * from './Header';
6
+ export * from './ChatHeader';
7
+ export * from './ChatHeaderMembers';
@@ -162,7 +162,7 @@
162
162
  .wxchtf-message-skeleton {
163
163
  display: flex;
164
164
  flex-flow: row nowrap;
165
- align-items: end;
165
+ align-items: flex-end;
166
166
 
167
167
  .wxchtf-avatar {
168
168
  margin-right: 15px;
@@ -201,7 +201,7 @@
201
201
  flex: none;
202
202
  display: flex;
203
203
  padding: 0 1rem;
204
- align-items: end;
204
+ align-items: flex-end;
205
205
 
206
206
  .ant-btn.ant-btn-icon-only .anticon {
207
207
  font-size: 1.5rem;
@@ -0,0 +1,45 @@
1
+ import React, { FC, PropsWithChildren, useContext } from 'react';
2
+
3
+ export type Customizable<TComponent extends React.ComponentType<any>, TKey extends string> = TComponent & {
4
+ Component: TComponent;
5
+ key: TKey;
6
+ };
7
+
8
+ const CustomizeContext = React.createContext<CustomizeProviderValue>(null!);
9
+
10
+ export interface CustomizeProviderValue {
11
+ [key: string]: React.ComponentType<any> | null;
12
+ }
13
+
14
+ export function CustomizeProvider(props: PropsWithChildren<{ value: CustomizeProviderValue }>) {
15
+ const { children, value } = props;
16
+ return <CustomizeContext.Provider value={value}>{children}</CustomizeContext.Provider>;
17
+ }
18
+
19
+ function useCustom<TComponent extends React.ComponentType>(key: string): TComponent | undefined | null {
20
+ const value = useContext(CustomizeContext);
21
+ return value[key] as TComponent | null | undefined;
22
+ }
23
+
24
+ export function customize<TComponent extends React.ComponentType<any>, TKey extends string>(
25
+ key: TKey,
26
+ Component: TComponent,
27
+ ): Customizable<TComponent, TKey> {
28
+ const Result: FC<any> = React.forwardRef<TComponent, any>((props: any, forwardRef) => {
29
+ const Custom = useCustom(key);
30
+
31
+ if (Custom === null) {
32
+ return null;
33
+ }
34
+
35
+ if (Custom) {
36
+ return <Custom {...props} ref={forwardRef} />;
37
+ }
38
+
39
+ return <Component {...props} ref={forwardRef} />;
40
+ });
41
+
42
+ Result.displayName = 'Customizable.' + Component.displayName;
43
+
44
+ return Object.assign(Result, { key, Component }) as any;
45
+ }
package/src/util/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './uniqBy';
2
2
  export * from './uniqId';
3
+ export * from './customize';