cursors!
This commit is contained in:
parent
36fc386269
commit
80d2ba5f00
9 changed files with 179 additions and 50 deletions
|
@ -7,11 +7,13 @@ import useCamera from 'hooks/useCamera'
|
||||||
import Defs from './defs'
|
import Defs from './defs'
|
||||||
import Page from './page'
|
import Page from './page'
|
||||||
import Brush from './brush'
|
import Brush from './brush'
|
||||||
|
import Cursor from './cursor'
|
||||||
import Bounds from './bounds/bounding-box'
|
import Bounds from './bounds/bounding-box'
|
||||||
import BoundsBg from './bounds/bounds-bg'
|
import BoundsBg from './bounds/bounds-bg'
|
||||||
import Handles from './bounds/handles'
|
import Handles from './bounds/handles'
|
||||||
import useCanvasEvents from 'hooks/useCanvasEvents'
|
import useCanvasEvents from 'hooks/useCanvasEvents'
|
||||||
import ContextMenu from './context-menu/context-menu'
|
import ContextMenu from './context-menu/context-menu'
|
||||||
|
import { deepCompareArrays } from 'utils'
|
||||||
|
|
||||||
function resetError() {
|
function resetError() {
|
||||||
null
|
null
|
||||||
|
@ -41,6 +43,7 @@ export default function Canvas(): JSX.Element {
|
||||||
<Bounds />
|
<Bounds />
|
||||||
<Handles />
|
<Handles />
|
||||||
<Brush />
|
<Brush />
|
||||||
|
<Peers />
|
||||||
</g>
|
</g>
|
||||||
)}
|
)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
@ -49,6 +52,31 @@ export default function Canvas(): JSX.Element {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Peers(): JSX.Element {
|
||||||
|
const peerIds = useSelector((s) => {
|
||||||
|
return s.data.room ? Object.keys(s.data.room?.peers) : []
|
||||||
|
}, deepCompareArrays)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{peerIds.map((id) => (
|
||||||
|
<Peer key={id} id={id} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Peer({ id }: { id: string }): JSX.Element {
|
||||||
|
const hasPeer = useSelector((s) => {
|
||||||
|
return s.data.room && s.data.room.peers[id] !== undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const point = useSelector(
|
||||||
|
(s) => hasPeer && s.data.room.peers[id].cursor.point
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Cursor point={point} />
|
||||||
|
}
|
||||||
const MainSVG = styled('svg', {
|
const MainSVG = styled('svg', {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
|
|
@ -1,28 +1,19 @@
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React from 'react'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
|
|
||||||
export default function Cursor(): JSX.Element {
|
export default function Cursor({
|
||||||
const rCursor = useRef<SVGSVGElement>(null)
|
color = 'dodgerblue',
|
||||||
|
point = [0, 0],
|
||||||
useEffect(() => {
|
}: {
|
||||||
function updatePosition(e: PointerEvent) {
|
color?: string
|
||||||
const cursor = rCursor.current
|
point: number[]
|
||||||
|
}): JSX.Element {
|
||||||
cursor.setAttribute(
|
const transform = `translate(${point[0] - 12} ${point[1] - 10})`
|
||||||
'transform',
|
|
||||||
`translate(${e.clientX - 12} ${e.clientY - 10})`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.addEventListener('pointermove', updatePosition)
|
|
||||||
return () => {
|
|
||||||
document.body.removeEventListener('pointermove', updatePosition)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledCursor
|
<StyledCursor
|
||||||
ref={rCursor}
|
color={color}
|
||||||
|
transform={transform}
|
||||||
width="35px"
|
width="35px"
|
||||||
height="35px"
|
height="35px"
|
||||||
viewBox="0 0 35 35"
|
viewBox="0 0 35 35"
|
||||||
|
@ -33,23 +24,19 @@ export default function Cursor(): JSX.Element {
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M12,24.4219 L12,8.4069 L23.591,20.0259 L16.81,20.0259 L16.399,20.1499 L12,24.4219 Z"
|
d="M12,24.4219 L12,8.4069 L23.591,20.0259 L16.81,20.0259 L16.399,20.1499 L12,24.4219 Z"
|
||||||
id="point-border"
|
fill="#ffffff"
|
||||||
fill="#FFFFFF"
|
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M21.0845,25.0962 L17.4795,26.6312 L12.7975,15.5422 L16.4835,13.9892 L21.0845,25.0962 Z"
|
d="M21.0845,25.0962 L17.4795,26.6312 L12.7975,15.5422 L16.4835,13.9892 L21.0845,25.0962 Z"
|
||||||
id="stem-border"
|
fill="#ffffff"
|
||||||
fill="#FFFFFF"
|
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M19.751,24.4155 L17.907,25.1895 L14.807,17.8155 L16.648,17.0405 L19.751,24.4155 Z"
|
d="M19.751,24.4155 L17.907,25.1895 L14.807,17.8155 L16.648,17.0405 L19.751,24.4155 Z"
|
||||||
id="stem"
|
fill="currentColor"
|
||||||
fill="#000000"
|
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M13,10.814 L13,22.002 L15.969,19.136 L16.397,18.997 L21.165,18.997 L13,10.814 Z"
|
d="M13,10.814 L13,22.002 L15.969,19.136 L16.397,18.997 L21.165,18.997 L13,10.814 Z"
|
||||||
id="point"
|
fill="currentColor"
|
||||||
fill="#000000"
|
|
||||||
/>
|
/>
|
||||||
</StyledCursor>
|
</StyledCursor>
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,7 +28,7 @@ function Shape({ id, isSelecting }: ShapeProps): JSX.Element {
|
||||||
|
|
||||||
const strokeWidth = useSelector((s) => {
|
const strokeWidth = useSelector((s) => {
|
||||||
const shape = getShape(s.data, id)
|
const shape = getShape(s.data, id)
|
||||||
const style = getShapeStyle(shape.style)
|
const style = getShapeStyle(shape?.style)
|
||||||
return +style.strokeWidth
|
return +style.strokeWidth
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
"next-auth": "^3.27.0",
|
"next-auth": "^3.27.0",
|
||||||
"next-pwa": "^5.2.21",
|
"next-pwa": "^5.2.21",
|
||||||
"perfect-freehand": "^0.4.91",
|
"perfect-freehand": "^0.4.91",
|
||||||
|
"pusher": "^5.0.0",
|
||||||
"pusher-js": "^7.0.3",
|
"pusher-js": "^7.0.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
|
33
pages/api/pusher-auth.ts
Normal file
33
pages/api/pusher-auth.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { NextApiHandler } from 'next'
|
||||||
|
import Pusher from 'pusher'
|
||||||
|
import { v4 as uuid } from 'uuid'
|
||||||
|
|
||||||
|
const pusher = new Pusher({
|
||||||
|
key: '5dc87c88b8684bda655a',
|
||||||
|
appId: '1226484',
|
||||||
|
secret: process.env.PUSHER_SECRET,
|
||||||
|
cluster: 'eu',
|
||||||
|
})
|
||||||
|
|
||||||
|
const PusherAuth: NextApiHandler = (req, res) => {
|
||||||
|
try {
|
||||||
|
const { socket_id, channel_name } = req.body
|
||||||
|
|
||||||
|
const presenceData = {
|
||||||
|
user_id: uuid(),
|
||||||
|
user_info: { name: 'Anonymous' },
|
||||||
|
}
|
||||||
|
|
||||||
|
const auth = pusher.authenticate(
|
||||||
|
socket_id.toString(),
|
||||||
|
channel_name.toString(),
|
||||||
|
presenceData
|
||||||
|
)
|
||||||
|
|
||||||
|
return res.send(auth)
|
||||||
|
} catch (err) {
|
||||||
|
res.status(403).end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PusherAuth
|
|
@ -2,19 +2,21 @@ import Pusher from 'pusher-js'
|
||||||
import * as PusherTypes from 'pusher-js'
|
import * as PusherTypes from 'pusher-js'
|
||||||
import state from 'state/state'
|
import state from 'state/state'
|
||||||
import { Shape } from 'types'
|
import { Shape } from 'types'
|
||||||
import { v4 as uuid } from 'uuid'
|
|
||||||
|
|
||||||
class RoomClient {
|
class RoomClient {
|
||||||
room: string
|
room: string
|
||||||
pusher: Pusher
|
pusher: Pusher
|
||||||
channel: PusherTypes.Channel
|
channel: PusherTypes.PresenceChannel
|
||||||
lastCursorEventTime = 0
|
lastCursorEventTime = 0
|
||||||
id = uuid()
|
id: string
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Create pusher instance and bind events
|
// Create pusher instance and bind events
|
||||||
|
|
||||||
this.pusher = new Pusher('5dc87c88b8684bda655a', { cluster: 'eu' })
|
this.pusher = new Pusher('5dc87c88b8684bda655a', {
|
||||||
|
cluster: 'eu',
|
||||||
|
authEndpoint: 'http://localhost:3000/api/pusher-auth',
|
||||||
|
})
|
||||||
|
|
||||||
this.pusher.connection.bind('connecting', () =>
|
this.pusher.connection.bind('connecting', () =>
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'connecting' })
|
state.send('RT_CHANGED_STATUS', { status: 'connecting' })
|
||||||
|
@ -28,33 +30,42 @@ class RoomClient {
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'unavailable' })
|
state.send('RT_CHANGED_STATUS', { status: 'unavailable' })
|
||||||
)
|
)
|
||||||
|
|
||||||
this.pusher.connection.bind('failed', () =>
|
this.pusher.connection.bind('failed', () => {
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'failed' })
|
state.send('RT_CHANGED_STATUS', { status: 'failed' })
|
||||||
)
|
})
|
||||||
|
|
||||||
this.pusher.connection.bind('disconnected', () =>
|
this.pusher.connection.bind('disconnected', () => {
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'disconnected' })
|
state.send('RT_CHANGED_STATUS', { status: 'disconnected' })
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(room: string) {
|
connect(roomId: string) {
|
||||||
this.room = room
|
this.room = 'presence-' + roomId
|
||||||
|
|
||||||
// Subscribe to channel
|
// Subscribe to channel
|
||||||
|
|
||||||
this.channel = this.pusher.subscribe(this.room)
|
this.channel = this.pusher.subscribe(
|
||||||
|
this.room
|
||||||
|
) as PusherTypes.PresenceChannel
|
||||||
|
|
||||||
this.channel.bind('pusher:subscription_error', () => {
|
this.channel.bind('pusher:subscription_error', (err: string) => {
|
||||||
|
console.warn(err)
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'subscription-error' })
|
state.send('RT_CHANGED_STATUS', { status: 'subscription-error' })
|
||||||
})
|
})
|
||||||
|
|
||||||
this.channel.bind('pusher:subscription_succeeded', () => {
|
this.channel.bind('pusher:subscription_succeeded', () => {
|
||||||
|
const me = this.channel.members.me
|
||||||
|
const userId = me.id
|
||||||
|
|
||||||
|
this.id = userId
|
||||||
|
|
||||||
state.send('RT_CHANGED_STATUS', { status: 'subscribed' })
|
state.send('RT_CHANGED_STATUS', { status: 'subscribed' })
|
||||||
})
|
})
|
||||||
|
|
||||||
this.channel.bind(
|
this.channel.bind(
|
||||||
'created_shape',
|
'created_shape',
|
||||||
(payload: { id: string; pageId: string; shape: Shape }) => {
|
(payload: { id: string; pageId: string; shape: Shape }) => {
|
||||||
|
if (payload.id === this.id) return
|
||||||
state.send('RT_CREATED_SHAPE', payload)
|
state.send('RT_CREATED_SHAPE', payload)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -62,6 +73,7 @@ class RoomClient {
|
||||||
this.channel.bind(
|
this.channel.bind(
|
||||||
'deleted_shape',
|
'deleted_shape',
|
||||||
(payload: { id: string; pageId: string; shape: Shape }) => {
|
(payload: { id: string; pageId: string; shape: Shape }) => {
|
||||||
|
if (payload.id === this.id) return
|
||||||
state.send('RT_DELETED_SHAPE', payload)
|
state.send('RT_DELETED_SHAPE', payload)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -69,12 +81,13 @@ class RoomClient {
|
||||||
this.channel.bind(
|
this.channel.bind(
|
||||||
'edited_shape',
|
'edited_shape',
|
||||||
(payload: { id: string; pageId: string; change: Partial<Shape> }) => {
|
(payload: { id: string; pageId: string; change: Partial<Shape> }) => {
|
||||||
|
if (payload.id === this.id) return
|
||||||
state.send('RT_EDITED_SHAPE', payload)
|
state.send('RT_EDITED_SHAPE', payload)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
this.channel.bind(
|
this.channel.bind(
|
||||||
'moved_cursor',
|
'client-moved-cursor',
|
||||||
(payload: { id: string; pageId: string; point: number[] }) => {
|
(payload: { id: string; pageId: string; point: number[] }) => {
|
||||||
if (payload.id === this.id) return
|
if (payload.id === this.id) return
|
||||||
state.send('RT_MOVED_CURSOR', payload)
|
state.send('RT_MOVED_CURSOR', payload)
|
||||||
|
@ -95,10 +108,10 @@ class RoomClient {
|
||||||
|
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
|
|
||||||
if (now - this.lastCursorEventTime > 42) {
|
if (now - this.lastCursorEventTime > 200) {
|
||||||
this.lastCursorEventTime = now
|
this.lastCursorEventTime = now
|
||||||
|
|
||||||
this.channel?.trigger('RT_MOVED_CURSOR', {
|
this.channel?.trigger('client-moved-cursor', {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
pageId,
|
pageId,
|
||||||
point,
|
point,
|
||||||
|
|
|
@ -188,6 +188,7 @@ const state = createState({
|
||||||
RT_EDITED_SHAPE: 'editRtShape',
|
RT_EDITED_SHAPE: 'editRtShape',
|
||||||
RT_MOVED_CURSOR: 'moveRtCursor',
|
RT_MOVED_CURSOR: 'moveRtCursor',
|
||||||
// Client
|
// Client
|
||||||
|
MOVED_POINTER: { secretlyDo: 'sendRtCursorMove' },
|
||||||
RESIZED_WINDOW: 'resetPageState',
|
RESIZED_WINDOW: 'resetPageState',
|
||||||
RESET_PAGE: 'resetPage',
|
RESET_PAGE: 'resetPage',
|
||||||
TOGGLED_READ_ONLY: 'toggleReadOnly',
|
TOGGLED_READ_ONLY: 'toggleReadOnly',
|
||||||
|
@ -1145,10 +1146,16 @@ const state = createState({
|
||||||
// Networked Room
|
// Networked Room
|
||||||
setRtStatus(data, payload: { id: string; status: string }) {
|
setRtStatus(data, payload: { id: string; status: string }) {
|
||||||
const { status } = payload
|
const { status } = payload
|
||||||
|
|
||||||
if (!data.room) {
|
if (!data.room) {
|
||||||
data.room = { id: null, status: '' }
|
data.room = {
|
||||||
|
id: null,
|
||||||
|
status: '',
|
||||||
|
peers: {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.room.peers = {}
|
||||||
data.room.status = status
|
data.room.status = status
|
||||||
},
|
},
|
||||||
addRtShape(data, payload: { pageId: string; shape: Shape }) {
|
addRtShape(data, payload: { pageId: string; shape: Shape }) {
|
||||||
|
@ -1166,8 +1173,26 @@ const state = createState({
|
||||||
// What if the page is in storage?
|
// What if the page is in storage?
|
||||||
Object.assign(data.document[pageId].shapes[shape.id], shape)
|
Object.assign(data.document[pageId].shapes[shape.id], shape)
|
||||||
},
|
},
|
||||||
moveRtCursor() {
|
sendRtCursorMove(data, payload: PointerInfo) {
|
||||||
null
|
const point = screenToWorld(payload.point, data)
|
||||||
|
pusher.moveCursor(data.currentPageId, point)
|
||||||
|
},
|
||||||
|
moveRtCursor(
|
||||||
|
data,
|
||||||
|
payload: { id: string; pageId: string; point: number[] }
|
||||||
|
) {
|
||||||
|
const { room } = data
|
||||||
|
|
||||||
|
if (room.peers[payload.id] === undefined) {
|
||||||
|
room.peers[payload.id] = {
|
||||||
|
id: payload.id,
|
||||||
|
cursor: {
|
||||||
|
point: payload.point,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
room.peers[payload.id].cursor.point = payload.point
|
||||||
},
|
},
|
||||||
clearRoom(data) {
|
clearRoom(data) {
|
||||||
data.room = undefined
|
data.room = undefined
|
||||||
|
@ -1201,9 +1226,10 @@ const state = createState({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
connectToRoom(data, payload: { id: string }) {
|
connectToRoom(data, payload: { id: string }) {
|
||||||
data.room = { id: payload.id, status: 'connecting' }
|
data.room = { id: payload.id, status: 'connecting', peers: {} }
|
||||||
pusher.connect(payload.id)
|
pusher.connect(payload.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
resetPageState(data) {
|
resetPageState(data) {
|
||||||
const pageState = data.pageStates[data.currentPageId]
|
const pageState = data.pageStates[data.currentPageId]
|
||||||
data.pageStates[data.currentPageId] = { ...pageState }
|
data.pageStates[data.currentPageId] = { ...pageState }
|
||||||
|
|
8
types.ts
8
types.ts
|
@ -17,6 +17,7 @@ export interface Data {
|
||||||
room?: {
|
room?: {
|
||||||
id: string
|
id: string
|
||||||
status: string
|
status: string
|
||||||
|
peers: Record<string, Peer>
|
||||||
}
|
}
|
||||||
currentStyle: ShapeStyles
|
currentStyle: ShapeStyles
|
||||||
activeTool: ShapeType | 'select'
|
activeTool: ShapeType | 'select'
|
||||||
|
@ -37,6 +38,13 @@ export interface Data {
|
||||||
/* Document */
|
/* Document */
|
||||||
/* -------------------------------------------------- */
|
/* -------------------------------------------------- */
|
||||||
|
|
||||||
|
export interface Peer {
|
||||||
|
id: string
|
||||||
|
cursor: {
|
||||||
|
point: number[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface TLDocument {
|
export interface TLDocument {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
|
37
yarn.lock
37
yarn.lock
|
@ -2243,6 +2243,13 @@ abab@^2.0.3, abab@^2.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
|
||||||
integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
|
integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
|
||||||
|
|
||||||
|
abort-controller@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||||
|
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||||
|
dependencies:
|
||||||
|
event-target-shim "^5.0.0"
|
||||||
|
|
||||||
acorn-globals@^6.0.0:
|
acorn-globals@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
|
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
|
||||||
|
@ -3831,6 +3838,11 @@ etag@1.8.1:
|
||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
|
event-target-shim@^5.0.0:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||||
|
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||||
|
|
||||||
events@^3.0.0:
|
events@^3.0.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||||
|
@ -4546,6 +4558,11 @@ is-arrayish@^0.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||||
|
|
||||||
|
is-base64@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-base64/-/is-base64-1.1.0.tgz#8ce1d719895030a457c59a7dcaf39b66d99d56b4"
|
||||||
|
integrity sha512-Nlhg7Z2dVC4/PTvIFkgVVNvPHSO2eR/Yd0XzhGiXCXEvWnptXlXa/clQ8aePPiMuxEGcWfzWbGw2Fe3d+Y3v1g==
|
||||||
|
|
||||||
is-bigint@^1.0.1:
|
is-bigint@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a"
|
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a"
|
||||||
|
@ -5971,7 +5988,7 @@ next@^11.0.1:
|
||||||
vm-browserify "1.1.2"
|
vm-browserify "1.1.2"
|
||||||
watchpack "2.1.1"
|
watchpack "2.1.1"
|
||||||
|
|
||||||
node-fetch@2.6.1, node-fetch@^2.6.0:
|
node-fetch@2.6.1, node-fetch@^2.6.0, node-fetch@^2.6.1:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
@ -6658,6 +6675,17 @@ pusher-js@^7.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
tweetnacl "^1.0.3"
|
tweetnacl "^1.0.3"
|
||||||
|
|
||||||
|
pusher@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pusher/-/pusher-5.0.0.tgz#3dc39ff527637a4b4597652357b0ec562514c8e6"
|
||||||
|
integrity sha512-YaSZHkukytHR9+lklJp4yefwfR4685kfS6pqrSDUxPj45Ga29lIgyN7Jcnsz+bN5WKwXaf2+4c/x/j3pzWIAkw==
|
||||||
|
dependencies:
|
||||||
|
abort-controller "^3.0.0"
|
||||||
|
is-base64 "^1.1.0"
|
||||||
|
node-fetch "^2.6.1"
|
||||||
|
tweetnacl "^1.0.0"
|
||||||
|
tweetnacl-util "^0.15.0"
|
||||||
|
|
||||||
querystring-es3@0.2.1, querystring-es3@^0.2.0:
|
querystring-es3@0.2.1, querystring-es3@^0.2.0:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||||
|
@ -7822,7 +7850,12 @@ tty-browserify@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
|
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
|
||||||
integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==
|
integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==
|
||||||
|
|
||||||
tweetnacl@^1.0.3:
|
tweetnacl-util@^0.15.0:
|
||||||
|
version "0.15.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b"
|
||||||
|
integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==
|
||||||
|
|
||||||
|
tweetnacl@^1.0.0, tweetnacl@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue