Skip to content

Commit

Permalink
✨ feat: Awareness support user config
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmclin2 committed Jan 25, 2024
1 parent 85a0535 commit 8ca5977
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 27 deletions.
54 changes: 54 additions & 0 deletions docs/pro-editor/demos/realtimeCollaboration/SessionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ColorPicker, Form, Input, Modal } from 'antd';
import React from 'react';

interface Values {
name: string;
color: string;
}

interface SessionFormProps {
open: boolean;
onCreate: (values: Values) => void;
onCancel: () => void;
}

export const SessionForm: React.FC<SessionFormProps> = ({ open, onCreate, onCancel }) => {
const [form] = Form.useForm();
return (
<Modal
open={open}
title="Join Session"
okText="Join"
onCancel={onCancel}
onOk={() => {
form
.validateFields()
.then(({ color, name }) => {
form.resetFields();
const colorHex = typeof color === 'string' ? color : color?.toHexString();
onCreate({ color: colorHex, name });
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<Form form={form} layout="vertical" name="form_in_modal">
<Form.Item
name="name"
label="Name"
rules={[{ required: true, message: 'Please input your name' }]}
>
<Input />
</Form.Item>
<Form.Item
name="color"
label="Color"
rules={[{ required: true, message: 'Please select your color' }]}
>
<ColorPicker defaultValue="#1677FF" format="hex" />
</Form.Item>
</Form>
</Modal>
);
};
47 changes: 38 additions & 9 deletions docs/pro-editor/demos/realtimeCollaboration/demo.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/**
* title: 实时协同
* description: 打开多个窗口,点击 "Join" 加入协作
*/
import { Awareness } from '@ant-design/pro-editor';
import { Button, Divider, Input } from 'antd';
import { memo } from 'react';
import { memo, useState } from 'react';
import { WebrtcProvider } from 'y-webrtc';

import { createStore, doc, Provider, useStore } from './store';
import { SessionForm } from './SessionForm';
import { Provider, createStore, doc, useStore } from './store';

const App = memo(() => {
const store = useStore((state) => ({
Expand All @@ -30,11 +34,36 @@ const App = memo(() => {
);
});

const Container = () => (
<Provider createStore={createStore}>
<Awareness provider={new WebrtcProvider('test-room', doc)} />
<App />
</Provider>
);
const Container = () => {
const [user, setUser] = useState(null);
const [open, setOpen] = useState(false);

const onCreate = (values) => {
setUser(values);
setOpen(false);
};

return (
<Provider createStore={createStore}>
<Button
type="primary"
onClick={() => {
setOpen(true);
}}
>
Join
</Button>
<SessionForm
open={open}
onCreate={onCreate}
onCancel={() => {
setOpen(false);
}}
/>
{user ? <Awareness provider={new WebrtcProvider('test-room', doc)} user={user} /> : null}
<App />
</Provider>
);
};

export default memo(Container);
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/styled": "^11.11.0",
"@faker-js/faker": "^7.6.0",
"@floating-ui/react": "^0.24.8",
"ahooks": "^3.7.8",
"classnames": "^2.3.2",
Expand Down
1 change: 0 additions & 1 deletion src/Awareness/Avatars/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Avatar as A } from 'antd';
import { memo } from 'react';
import { shallow } from 'zustand/shallow';

import { useStore } from '../store';
import Avatar from './Avatar';

Expand Down
12 changes: 7 additions & 5 deletions src/Awareness/Awareness.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { memo } from 'react';
import type { WebrtcProvider } from 'y-webrtc';

import Avatars from './Avatars';
import Cursors from './Cursors';

import { createStore, Provider } from './store';
import { createStore, Provider, User } from './store';

export interface AwarenessProps {
/**
Expand All @@ -19,10 +17,14 @@ export interface AwarenessProps {
* 是否显示用户游标
*/
cursors?: boolean;
/**
* 用户名和颜色设置
*/
user: Pick<User, 'color' | 'name'>;
}

const Awareness = memo<AwarenessProps>(({ provider, avatars = true, cursors = true }) => (
<Provider createStore={() => createStore(provider)}>
const Awareness = memo<AwarenessProps>(({ provider, avatars = true, cursors = true, user }) => (
<Provider createStore={() => createStore(provider, user)}>
{cursors && <Cursors />}
{avatars && <Avatars />}
</Provider>
Expand Down
2 changes: 1 addition & 1 deletion src/Awareness/Cursors/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useStore } from '../store';

const Cursors = memo(() => {
const awarenessStates = useStore<AwarenessState[]>(
(s) => s.awarenessStates?.filter((a) => a.active && a.user.name !== s.currentUser),
(s) => s.awarenessStates?.filter((a) => a.active && a.user.id !== s.currentUser.id),
isEqual,
);

Expand Down
18 changes: 8 additions & 10 deletions src/Awareness/store.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// FIXME:这里理论上不应该使用 faker 的,后续需要重构优化掉
import { faker } from '@faker-js/faker';
import isEqual from 'fast-deep-equal';
import { nanoid } from 'nanoid';
import { useEffect } from 'react';
import type { Position } from 'react-rnd';
import type { Awareness } from 'y-protocols/awareness';
import type { WebrtcProvider } from 'y-webrtc';
import type { StoreApi } from 'zustand';
import { createContext } from 'zustand-utils';
import { createWithEqualityFn } from 'zustand/traditional';

import { useAwarenessEvent } from './event';

export interface User {
Expand All @@ -26,19 +25,22 @@ export interface AwarenessState {
interface ProviderStore {
provider: WebrtcProvider;
awareness?: Awareness;
currentUser: string;
currentUser: User;
awarenessStates: AwarenessState[];
followUser?: string;

setFollowUser: (id: string) => void;
}

export const createStore = (provider: WebrtcProvider) => {
export const createStore = (provider: WebrtcProvider, user: Pick<User, 'color' | 'name'>) => {
const useStore = createWithEqualityFn<ProviderStore>((set) => {
return {
provider,
awareness: provider.awareness,
currentUser: faker.name.fullName(),
currentUser: {
id: nanoid(),
...user,
},
awarenessStates: [],

setFollowUser: (followUser) => {
Expand Down Expand Up @@ -68,11 +70,7 @@ export const createStore = (provider: WebrtcProvider) => {
});

// 再初始化一轮用户
awareness.setLocalStateField('user', {
id: useStore.getState().currentUser,
name: useStore.getState().currentUser,
color: faker.color.rgb(),
});
awareness.setLocalStateField('user', useStore.getState().currentUser);

awareness.setLocalStateField('active', true);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down

0 comments on commit 8ca5977

Please sign in to comment.