Skip to content

Commit 4c19a12

Browse files
committed
feat: basic message service
1 parent c7f83a0 commit 4c19a12

File tree

13 files changed

+163
-40
lines changed

13 files changed

+163
-40
lines changed

packages/webrtc-im/client/component/avatar/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const Avatar: FC<{
2828
for (let i = 0; i < rows; i++) {
2929
map[i] = [];
3030
for (let k = 0; k < halfRows; k++) {
31-
map[i][k] = num % (i * k + 1) > 2 ? color : "";
31+
map[i][k] = num % (i * k + 1) > 1 ? color : "";
3232
}
3333
for (let k = halfRows; k < rows; k++) {
3434
map[i][k] = map[i][rows - 1 - k];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.text {
2+
display: inline-block;
3+
max-width: 100%;
4+
overflow: hidden;
5+
text-overflow: ellipsis;
6+
white-space: nowrap;
7+
}
Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
11
import { cs } from "@block-kit/utils";
22
import type { FC } from "react";
3+
import { useState } from "react";
4+
import styles from "./index.m.scss";
5+
import { Tooltip } from "@arco-design/web-react";
36

4-
export const Ellipsis: FC<{
7+
export const EllipsisTooltip: FC<{
8+
/** 样式 */
59
className?: string;
10+
/** 内部文本 */
11+
text: string;
12+
/** ToolTip 文本/节点 */
13+
tooltip: React.ReactNode;
614
}> = props => {
7-
return <div className={cs(props.className)}></div>;
15+
const [tooltipVisible, setTooltipVisible] = useState(false);
16+
17+
const onRef = (el: HTMLSpanElement | null) => {
18+
if (!el) return;
19+
const isOverflowing = el.scrollWidth > el.clientWidth;
20+
setTooltipVisible(isOverflowing);
21+
};
22+
23+
return (
24+
<Tooltip disabled={!tooltipVisible} content={props.tooltip}>
25+
<span ref={onRef} className={cs(styles.text, props.className)}>
26+
{props.text}
27+
</span>
28+
</Tooltip>
29+
);
830
};

packages/webrtc-im/client/service/message.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,28 @@ import type { WebRTCService } from "./webrtc";
77
import { atoms } from "../store/atoms";
88
import { Bind } from "@block-kit/utils";
99
import { SERVER_EVENT } from "../../types/signaling";
10+
import { WEBRTC_EVENT } from "../../types/webrtc";
1011

1112
export class MessageService {
1213
public readonly listAtom: PrimitiveAtom<TransferEntry[]>;
1314

1415
constructor(public signal: SignalService, public rtc: WebRTCService) {
1516
this.listAtom = atom<TransferEntry[]>([]);
16-
this.rtc.onConnectionStateChange = this.onRTCStateChange;
1717
this.signal.socket.on("connect", this.onSignalConnected);
1818
this.signal.socket.on("disconnect", this.onSignalDisconnected);
1919
this.signal.on(SERVER_EVENT.SEND_OFFER, this.onReceiveOffer);
20+
this.signal.on(SERVER_EVENT.SEND_ICE, this.onReceiveIce);
2021
this.signal.on(SERVER_EVENT.SEND_ANSWER, this.onReceiveAnswer);
22+
this.rtc.bus.on(WEBRTC_EVENT.STATE_CHANGE, this.onRTCStateChange);
2123
}
2224

2325
public destroy() {
24-
this.rtc.onConnectionStateChange = void 0;
2526
this.signal.socket.off("connect", this.onSignalConnected);
2627
this.signal.socket.off("disconnect", this.onSignalDisconnected);
2728
this.signal.off(SERVER_EVENT.SEND_OFFER, this.onReceiveOffer);
29+
this.signal.off(SERVER_EVENT.SEND_ICE, this.onReceiveIce);
2830
this.signal.off(SERVER_EVENT.SEND_ANSWER, this.onReceiveAnswer);
31+
this.rtc.bus.off(WEBRTC_EVENT.STATE_CHANGE, this.onRTCStateChange);
2932
}
3033

3134
public addEntry(entry: TransferEntry) {
@@ -34,10 +37,6 @@ export class MessageService {
3437
atoms.set(this.listAtom, newList);
3538
}
3639

37-
public clearEntries() {
38-
atoms.set(this.listAtom, []);
39-
}
40-
4140
public addSystemEntry(data: string) {
4241
this.addEntry({ key: TRANSFER_TYPE.SYSTEM, data });
4342
}
@@ -50,6 +49,10 @@ export class MessageService {
5049
this.addEntry({ key: TRANSFER_TYPE.FILE, ...data });
5150
}
5251

52+
public clearEntries() {
53+
atoms.set(this.listAtom, []);
54+
}
55+
5356
@Bind
5457
private onSignalConnected() {
5558
this.addSystemEntry("Signal Connected");
@@ -78,11 +81,16 @@ export class MessageService {
7881

7982
@Bind
8083
private onReceiveOffer() {
81-
this.addSystemEntry(`Received RTC Offer`);
84+
this.addSystemEntry("Received RTC Offer");
85+
}
86+
87+
@Bind
88+
private onReceiveIce() {
89+
this.addSystemEntry("Received RTC ICE");
8290
}
8391

8492
@Bind
8593
private onReceiveAnswer() {
86-
this.addSystemEntry(`Received RTC Answer`);
94+
this.addSystemEntry("Received RTC Answer");
8795
}
8896
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { atom } from "jotai";
22
import type { NetType } from "../../types/client";
33
import { NET_TYPE } from "../../types/client";
4+
import type { ServerJoinRoomEvent } from "../../types/signaling";
45

56
export class StoreService {
67
/** 列表页网络 Tab */
78
public netTypeAtom = atom<NetType>(NET_TYPE.LAN);
89
/** 匹配的 UserId */
910
public peerIdAtom = atom<string>("");
11+
/** 用户列表 */
12+
public userListAtom = atom<ServerJoinRoomEvent>([]);
1013
}

packages/webrtc-im/client/service/webrtc.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { CLINT_EVENT, SERVER_EVENT } from "../../types/signaling";
1010
import { Bind } from "@block-kit/utils";
1111
import { ERROR_CODE } from "../../types/server";
1212
import { atoms } from "../store/atoms";
13+
import { EventBus } from "../utils/event-bus";
14+
import type { WebRTCEvent } from "../../types/webrtc";
15+
import { WEBRTC_EVENT } from "../../types/webrtc";
1316

1417
export class WebRTCService {
1518
/** 连接状态 */
@@ -20,11 +23,14 @@ export class WebRTCService {
2023
public channel: RTCDataChannel;
2124
/** RTC 连接实例 */
2225
public connection: RTCPeerConnection;
26+
/** 事件总线 */
27+
public bus: EventBus<WebRTCEvent>;
2328

2429
constructor(public signal: SignalService) {
2530
const rtc = this.createRTCPeerConnection();
2631
this.channel = rtc.channel;
2732
this.connection = rtc.connection;
33+
this.bus = new EventBus<WebRTCEvent>();
2834
this.connectedPromise = createConnectReadyPromise();
2935
this.stateAtom = atom<ConnectionState>(CONNECTION_STATE.READY);
3036
this.signal.on(SERVER_EVENT.SEND_OFFER, this.onReceiveOffer);
@@ -80,12 +86,6 @@ export class WebRTCService {
8086
return this.connectedPromise;
8187
}
8288

83-
public onOpen?: (event: Event) => void;
84-
public onMessage?: (event: MessageEvent) => void;
85-
public onError?: (event: RTCErrorEvent) => void;
86-
public onClose?: (event: Event) => void;
87-
public onConnectionStateChange?: () => void;
88-
8989
private createRTCPeerConnection(ice?: string) {
9090
const RTCPeerConnection =
9191
// @ts-expect-error RTCPeerConnection
@@ -116,16 +116,16 @@ export class WebRTCService {
116116
});
117117
connection.ondatachannel = event => {
118118
const channel = event.channel;
119-
channel.onopen = e => this.onOpen && this.onOpen(e);
120-
channel.onmessage = e => this.onMessage && this.onMessage(e);
121-
channel.onerror = e => this.onError && this.onError(e as RTCErrorEvent);
122-
channel.onclose = e => this.onClose && this.onClose(e);
119+
channel.onopen = e => this.bus.emit(WEBRTC_EVENT.OPEN, e);
120+
channel.onmessage = e => this.bus.emit(WEBRTC_EVENT.MESSAGE, e);
121+
channel.onerror = e => this.bus.emit(WEBRTC_EVENT.ERROR, e as RTCErrorEvent);
122+
channel.onclose = e => this.bus.emit(WEBRTC_EVENT.CLOSE, e);
123123
};
124124
connection.onconnectionstatechange = () => {
125125
if (this.connection.connectionState === "connected") {
126126
atoms.set(this.stateAtom, CONNECTION_STATE.CONNECTED);
127127
}
128-
this.onConnectionStateChange && this.onConnectionStateChange();
128+
this.bus.emit(WEBRTC_EVENT.STATE_CHANGE, this);
129129
};
130130
return { connection, channel };
131131
}

packages/webrtc-im/client/styles/contacts.m.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@import '../../node_modules/@block-kit/utils/dist//style/vars.scss';
1+
@import '../../node_modules/@block-kit/utils/dist/style/vars';
22

33
.container {
44
border-right: 1px solid var(--color-border-2);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
1+
@import '../../node_modules/@block-kit/utils/dist/style/vars';
2+
13
.container {
4+
display: flex;
25
flex: 1 0;
6+
flex-direction: column;
7+
}
8+
9+
.captainArea {
10+
align-items: center;
11+
border-bottom: 1px solid var(--color-fill-1);
12+
box-sizing: border-box;
13+
display: flex;
14+
gap: 10px;
15+
height: 60px;
16+
padding: 10px;
17+
18+
.captainName {
19+
font-weight: bold;
20+
margin-top: 1px;
21+
}
22+
}
23+
24+
.dot {
25+
border-radius: 50%;
26+
height: 8px;
27+
width: 8px;
28+
}
29+
30+
.messageArea {
31+
@include no-scrollbar;
32+
33+
flex: 1 0;
34+
overflow-y: auto;
35+
padding: 5px 10px;
36+
}
37+
38+
.inputArea {
39+
border-top: 1px solid var(--color-fill-1);
40+
height: 100px;
341
}

packages/webrtc-im/client/styles/tab-bar.m.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
}
1919

2020
.name {
21+
box-sizing: border-box;
2122
color: var(--color-text-2);
2223
font-size: 11px;
2324
margin-top: 8px;
25+
max-width: 100%;
26+
padding: 0 5px;
2427
}
2528

2629
.netTab {

packages/webrtc-im/client/view/contacts.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import { cs } from "@block-kit/utils";
2020

2121
export const Contacts: FC = () => {
2222
const { signal, store, message, rtc } = useGlobalContext();
23-
const [list, setList] = useState<ServerJoinRoomEvent>([]);
2423
const [search, setSearch] = useState("");
2524
const netType = useAtomValue(store.netTypeAtom);
2625
const [peerId, setPeerId] = useAtom(store.peerIdAtom);
26+
const [list, setList] = useAtom(store.userListAtom);
2727

2828
const onInitUser = useMemoFn(() => {
2929
setList([]);
@@ -51,7 +51,7 @@ export const Contacts: FC = () => {
5151
let isLan = signal.hash === user.hash;
5252
// 本地部署应用时, ip 地址可能是 ::1 或 ::ffff:
5353
if (
54-
(!isLan && signal.ip === ":*:*") ||
54+
signal.ip === ":*:*" ||
5555
signal.ip.startsWith("192.168") ||
5656
signal.ip.startsWith("10.") ||
5757
signal.ip.startsWith("172.") ||
@@ -67,21 +67,24 @@ export const Contacts: FC = () => {
6767
rtc.disconnect();
6868
message.clearEntries();
6969
setPeerId(userId);
70-
await signal.isConnected();
7170
message.addSystemEntry("Connecting " + userId);
71+
await signal.isConnected();
7272
rtc.connect(userId);
7373
};
7474

7575
const onReceiveOffer = useMemoFn(async (event: ServerSendOfferEvent) => {
7676
const { from } = event;
77+
// 这个实际上是先于实际 setRemoteDescription 的, 事件调用优先级会更高
7778
if (
78-
rtc.connection.connectionState !== "connected" &&
79-
rtc.connection.connectionState !== "connecting"
79+
peerId === from ||
80+
rtc.connection.connectionState === "new" ||
81+
rtc.connection.connectionState === "failed" ||
82+
rtc.connection.connectionState === "disconnected" ||
83+
rtc.connection.connectionState === "closed"
8084
) {
81-
message.clearEntries();
8285
setPeerId(from);
86+
message.clearEntries();
8387
message.addSystemEntry("Connecting " + from);
84-
await signal.isConnected();
8588
}
8689
});
8790

0 commit comments

Comments
 (0)