collect values from multiple users
This commit is contained in:
parent
d34955e5dd
commit
d6524330cc
13
src/App.tsx
13
src/App.tsx
|
|
@ -5,6 +5,7 @@ import { AgentButton } from "./components/AgentButton";
|
||||||
import { SocialShapeUtil } from "./SocialShapeUtil";
|
import { SocialShapeUtil } from "./SocialShapeUtil";
|
||||||
import { SocialShapeTool } from "./SocialShapeTool";
|
import { SocialShapeTool } from "./SocialShapeTool";
|
||||||
import { CustomToolbar, overrides } from "./ui";
|
import { CustomToolbar, overrides } from "./ui";
|
||||||
|
import { getDocumentMeta, getUserId, getUsersInRoom, setDocumentMeta } from "./storeUtils";
|
||||||
|
|
||||||
const shapeUtils = [SocialShapeUtil];
|
const shapeUtils = [SocialShapeUtil];
|
||||||
const tools = [SocialShapeTool];
|
const tools = [SocialShapeTool];
|
||||||
|
|
@ -34,6 +35,18 @@ export default function Canvas() {
|
||||||
onMount={(editor) => {
|
onMount={(editor) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
editor.getStateDescendant('select.idle').handleDoubleClickOnCanvas = () => void null;
|
editor.getStateDescendant('select.idle').handleDoubleClickOnCanvas = () => void null;
|
||||||
|
console.log(editor.store.allRecords())
|
||||||
|
const userId = getUserId(editor)
|
||||||
|
setDocumentMeta(editor, {
|
||||||
|
[userId]: 123
|
||||||
|
})
|
||||||
|
// console.log(getDocumentMeta(editor))
|
||||||
|
// removeDocumentMeta(editor, 'test')
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log(getDocumentMeta(editor))
|
||||||
|
console.log(getUsersInRoom(editor))
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
components={{
|
components={{
|
||||||
SharePanel: AgentButton,
|
SharePanel: AgentButton,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
resizeBox,
|
resizeBox,
|
||||||
toDomPrecision,
|
toDomPrecision,
|
||||||
} from 'tldraw'
|
} from 'tldraw'
|
||||||
|
import { getUserId } from './storeUtils'
|
||||||
|
|
||||||
export type ValueType = "scalar" | "boolean" | null
|
export type ValueType = "scalar" | "boolean" | null
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ export type ISocialShape = TLBaseShape<
|
||||||
text: string
|
text: string
|
||||||
selector: string
|
selector: string
|
||||||
valueType: ValueType
|
valueType: ValueType
|
||||||
|
values: Record<string, any>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
@ -27,7 +29,7 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
||||||
override canBind = () => false
|
override canBind = () => false
|
||||||
override canEdit = () => false
|
override canEdit = () => false
|
||||||
override getDefaultProps(): ISocialShape['props'] {
|
override getDefaultProps(): ISocialShape['props'] {
|
||||||
return { w: 160 * 2, h: 90 * 2, text: '', selector: '', valueType: null }
|
return { w: 160 * 2, h: 90 * 2, text: '', selector: '', valueType: null, values: {} }
|
||||||
}
|
}
|
||||||
override onResize: TLOnResizeHandler<ISocialShape> = (shape, info) => {
|
override onResize: TLOnResizeHandler<ISocialShape> = (shape, info) => {
|
||||||
return resizeBox(shape, info)
|
return resizeBox(shape, info)
|
||||||
|
|
@ -39,6 +41,7 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
||||||
isFilled: true,
|
isFilled: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
indicator(shape: ISocialShape) {
|
indicator(shape: ISocialShape) {
|
||||||
const bounds = this.editor.getShapeGeometry(shape).bounds
|
const bounds = this.editor.getShapeGeometry(shape).bounds
|
||||||
return (
|
return (
|
||||||
|
|
@ -50,16 +53,22 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override component(machine: ISocialShape) {
|
override component(shape: ISocialShape) {
|
||||||
|
const currentUser = getUserId(this.editor)
|
||||||
|
|
||||||
|
const handleOnChange = (newValue: any) => {
|
||||||
|
this.updateProps(shape, { values: { ...shape.props.values, [currentUser]: newValue } })
|
||||||
|
console.log(shape.props.values)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HTMLContainer style={{ padding: 4, borderRadius: 4, border: '1px solid #ccc', pointerEvents: 'all' }}>
|
<HTMLContainer style={{ padding: 4, borderRadius: 4, border: '1px solid #ccc' }} onPointerDown={(e) => e.stopPropagation()}>
|
||||||
<textarea style={{ width: '100%', height: '100%', border: 'none', outline: 'none', resize: 'none' }} value={machine.props.text} onChange={(e) => this.updateProps(machine, { text: e.target.value })} />
|
<textarea style={{ width: '100%', height: '50%', border: 'none', outline: 'none', resize: 'none', pointerEvents: 'all' }} value={shape.props.text} onChange={(e) => this.updateProps(shape, { text: e.target.value })} />
|
||||||
|
<ValueInterface type='boolean' value={shape.props.values[currentUser] ?? false} values={shape.props.values} onChange={handleOnChange} />
|
||||||
</HTMLContainer>
|
</HTMLContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private updateProps(shape: ISocialShape, props: Partial<ISocialShape['props']>) {
|
private updateProps(shape: ISocialShape, props: Partial<ISocialShape['props']>) {
|
||||||
this.editor.updateShape<ISocialShape>({
|
this.editor.updateShape<ISocialShape>({
|
||||||
id: shape.id,
|
id: shape.id,
|
||||||
|
|
@ -70,18 +79,23 @@ export class SocialShapeUtil extends BaseBoxShapeUtil<ISocialShape> {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// static removeParticipantsNotInRoom(editor: Editor, shapeId: TLShapeId) {
|
|
||||||
// const roomMembers = getRoomMembers(editor)
|
|
||||||
// const _shape = editor.getShape(shapeId)
|
|
||||||
// if (!_shape || _shape.type !== 'gmachine') return
|
|
||||||
// const shape = _shape as IGMachineShape
|
|
||||||
// const participants = new Map(shape.props.participants.map(p => [p.id, p]))
|
|
||||||
// participants.forEach((participant: any) => {
|
|
||||||
// if (!roomMembers.some(rm => rm.id === participant.id)) {
|
|
||||||
// participants.delete(participant.id)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// updateProps(editor, shape, { participants: Array.from(participants.values()) })
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ValueInterface({ type, value, values, onChange }: { type: ValueType; value: any; values: Record<string, any>; onChange: (value: any) => void }) {
|
||||||
|
switch (type) {
|
||||||
|
case 'boolean':
|
||||||
|
return <>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '4px' }}>
|
||||||
|
<input style={{ pointerEvents: 'all', width: '20px', height: '20px', margin: 0 }} type="checkbox" value={value} onChange={(e) => onChange(e.target.checked)} />
|
||||||
|
<div style={{ width: '1px', height: '20px', backgroundColor: 'grey' }} />
|
||||||
|
{Object.values(values).map((bool, i) => (
|
||||||
|
<div key={`boolean-${i}`} style={{ backgroundColor: bool ? 'blue' : 'white', width: '20px', height: '20px', border: '1px solid grey', borderRadius: 2 }} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
case 'scalar':
|
||||||
|
return <div>Slider</div>
|
||||||
|
default:
|
||||||
|
return <div>No Interface...</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import React from "react";
|
// import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
// <React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
// </React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Editor, RecordId, TLDocument } from "tldraw";
|
||||||
|
|
||||||
|
const docId = 'document:document' as RecordId<TLDocument>
|
||||||
|
|
||||||
|
export function setDocumentMeta(editor: Editor, meta: Record<string, any>) {
|
||||||
|
const updateFunc = (doc: TLDocument) => {
|
||||||
|
return {
|
||||||
|
...doc,
|
||||||
|
meta: {
|
||||||
|
...doc.meta,
|
||||||
|
...meta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.store.update(docId, updateFunc)
|
||||||
|
}
|
||||||
|
export function getDocumentMeta(editor: Editor) {
|
||||||
|
const document = editor.store.get(docId)
|
||||||
|
if (!document) return
|
||||||
|
return document.meta
|
||||||
|
}
|
||||||
|
export function removeDocumentMeta(editor: Editor, key: string) {
|
||||||
|
const updateFunc = (doc: TLDocument) => {
|
||||||
|
const { [key]: _, ...restMeta } = doc.meta;
|
||||||
|
return {
|
||||||
|
...doc,
|
||||||
|
meta: restMeta
|
||||||
|
};
|
||||||
|
};
|
||||||
|
editor.store.update(docId, updateFunc);
|
||||||
|
}
|
||||||
|
export function getUserId(editor: Editor) {
|
||||||
|
return editor.user.getId()
|
||||||
|
}
|
||||||
|
export function setUserPreferences(editor: Editor, name: string, color: string) {
|
||||||
|
editor.user.updateUserPreferences({
|
||||||
|
name,
|
||||||
|
color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getUsersInRoom(editor: Editor) {
|
||||||
|
return editor.getCollaborators().map(c=> c.userId)
|
||||||
|
}
|
||||||
3
todo.md
3
todo.md
|
|
@ -18,7 +18,8 @@ agents: {
|
||||||
|
|
||||||
social shape props:
|
social shape props:
|
||||||
```json
|
```json
|
||||||
function: string // e.g. "@all sum(SCALAR)"
|
text: string // e.g. "@all sum(SCALAR)"
|
||||||
selector: string // e.g. "@all"
|
selector: string // e.g. "@all"
|
||||||
valueType: string // e.g. "SCALAR"
|
valueType: string // e.g. "SCALAR"
|
||||||
|
values: Record<string, any>
|
||||||
```
|
```
|
||||||
Loading…
Reference in New Issue