Skip to content

Commit

Permalink
[base] Move presence into @sanity/base
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent 88a4c4a commit 4efefc4
Show file tree
Hide file tree
Showing 118 changed files with 1,064 additions and 1,011 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"normalize-pkgfields": "node scripts/normalizePackageFields.js",
"prettify": "lerna run clean && prettier --write \"**/*.js\"",
"postinstall": "lerna bootstrap",
"vercel-build": "npm run build && (cd packages/design-studio && node ../@sanity/cli/bin/sanity build -y) && mv packages/design-studio/dist packages/test-studio/dist"
"vercel-build": "npm run build && (cd packages/design-studio && node ../@sanity/cli/bin/sanity build -y) && mv packages/design-studio/dist packages/test-studio/dist",
"type-check": "lerna run --parallel type-check"
},
"devDependencies": {
"@babel/cli": "^7.7.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/base/components.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = require('./lib/components-public-api')
module.exports = require('./lib/components')
2 changes: 1 addition & 1 deletion packages/@sanity/base/components.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './src/components-public-api'
export * from './src/components'
3 changes: 2 additions & 1 deletion packages/@sanity/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"clean": "rimraf lib",
"coverage": "nyc --report=lcov --report=text _mocha -- test/**/*.test.js",
"test": "jest"
"test": "jest",
"type-check": "tsc --noEmit"
},
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/@sanity/base/presence.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
module.exports = require('./lib/presence/public-api')
module.exports = require('./lib/presence')
1 change: 1 addition & 0 deletions packages/@sanity/base/presence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src/presence'
10 changes: 10 additions & 0 deletions packages/@sanity/base/src/@types/parts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ declare module 'part:@sanity/base/schema' {

export default schema
}

declare module 'part:@sanity/components/avatar' {
export * from '@sanity/components/src/avatar'
}

declare module 'part:@sanity/components/tooltip' {
export * from '@sanity/components/src/tooltip'
}

declare module 'all:part:*'
1 change: 0 additions & 1 deletion packages/@sanity/base/src/components-public-api.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable react/require-default-props */

import React, {useState} from 'react'
import {useUser, useUserColor} from '@sanity/base/hooks'
import {User} from '../presence/types'
import {Avatar} from './Avatar'
import {AvatarPosition, AvatarSize, AvatarStatus} from './types'
import {Avatar, AvatarPosition, AvatarSize, AvatarStatus} from 'part:@sanity/components/avatar'
import {User} from '../datastores/user/types'
import {useUser, useUserColor} from '../hooks'

interface BaseProps {
position?: AvatarPosition
Expand Down
1 change: 1 addition & 0 deletions packages/@sanity/base/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './UserAvatar'
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-use-before-define */
import {map, mergeMapTo, share, switchMapTo, tap, switchMap} from 'rxjs/operators'
import {Transport, TransportEvent, TransportMessage} from './transport'
import {PresenceLocation} from '../types'
import {EMPTY, fromEvent, merge, Observable, defer} from 'rxjs'
import {PresenceLocation} from '../../../presence/types'
import {Transport, TransportEvent, TransportMessage} from './transport'

type BifurStateMessage = {
type: 'state'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Observable} from 'rxjs'
import {PresenceLocation} from '../types'
import {PresenceLocation} from '../../../presence/types'

// Not sure if this is the best choice of words, but:
// Message: something you send
Expand Down
11 changes: 6 additions & 5 deletions packages/@sanity/base/src/datastores/presence/presence-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,28 @@ import {
withLatestFrom
} from 'rxjs/operators'
import {flatten, groupBy, omit, uniq} from 'lodash'
import {createBifurTransport} from './message-transports/bifurTransport'
import {nanoid} from 'nanoid'

import userStore from '../user'
import userStore, {User} from '../user'
import {
PresenceLocation,
Session,
User,
// User,
UserSessionPair,
DocumentPresence,
GlobalPresence
} from './types'
} from '../../presence'

import {bifur} from '../../client/bifur'
import {connectionStatus$} from '../../connection-status/connection-status-store'
import {
DisconnectEvent,
RollCallEvent,
StateEvent,
TransportEvent
} from './message-transports/transport'
import {mock$} from './mock-events'
import {connectionStatus$} from '../../connection-status/connection-status-store'
import {createBifurTransport} from './message-transports/bifurTransport'

const KEY = 'presence_session_id'
const generate = () => nanoid(16)
Expand Down
43 changes: 0 additions & 43 deletions packages/@sanity/base/src/datastores/presence/types.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useState, useEffect} from 'react'
import {DocumentPresence} from '../../presence/types'
import {documentPresence} from './presence-store'
import {DocumentPresence} from './types'

export function useDocumentPresence(documentId): DocumentPresence[] {
const [presence, setPresence] = useState<DocumentPresence[]>([])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useState, useEffect} from 'react'
import {GlobalPresence} from '../../presence/types'
import {globalPresence$} from './presence-store'
import {GlobalPresence} from './types'

export function useGlobalPresence(): GlobalPresence[] {
const [presence, setPresence] = useState<GlobalPresence[]>([])
Expand Down
2 changes: 2 additions & 0 deletions packages/@sanity/base/src/datastores/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import createUserStore from './createUserStore'

export default createUserStore()

export * from './types'
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/* eslint-disable @typescript-eslint/no-use-before-define,react/no-multi-comp */
/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint-disable react/no-multi-comp */
/* eslint-disable react/require-default-props */

import React, {useContext} from 'react'
import {sortBy, uniqBy} from 'lodash'
import {AvatarPosition} from '../avatar'
import {AvatarCounter, AvatarPosition} from 'part:@sanity/components/avatar'
import {UserAvatar} from '../components/UserAvatar'
import {
AVATAR_DISTANCE,
AVATAR_SIZE,
Expand All @@ -11,11 +15,9 @@ import {
import {splitRight} from './utils'
import styles from './FieldPresence.css'
import {PresenceRegion} from './overlay/PresenceOverlayRegion'
import UserAvatar from './UserAvatar'
import {FormFieldPresenceContext} from './context'
import {PresenceTooltip} from './PresenceTooltip'
import {FormFieldPresence} from './types'
import {PresenceListItem} from './PresenceListItem'
import {Context} from './context'
import {PopoverList, StackCounter} from './index'

export interface FieldPresenceProps {
presence: FormFieldPresence[]
Expand All @@ -28,7 +30,7 @@ function FieldPresencePlaceholder(props: FieldPresenceProps) {
}

function FieldPresenceWithOverlay(props: FieldPresenceProps) {
const contextPresence = useContext(Context)
const contextPresence = useContext(FormFieldPresenceContext)
const {presence = contextPresence, maxAvatars = DEFAULT_MAX_AVATARS_FIELDS} = props
return presence.length > 0 ? (
<PresenceRegion
Expand All @@ -40,7 +42,7 @@ function FieldPresenceWithOverlay(props: FieldPresenceProps) {
}

function FieldPresenceWithoutOverlay(props: FieldPresenceProps) {
const contextPresence = useContext(Context)
const contextPresence = useContext(FormFieldPresenceContext)
const {presence = contextPresence, maxAvatars = DEFAULT_MAX_AVATARS_FIELDS} = props
return presence.length > 0 ? (
<FieldPresenceInner presence={presence} maxAvatars={maxAvatars} />
Expand All @@ -52,7 +54,7 @@ export const FieldPresence = DISABLE_OVERLAY
: FieldPresenceWithOverlay

interface InnerProps {
maxAvatars: number
maxAvatars?: number
presence: FormFieldPresence[]
stack?: boolean
position?: AvatarPosition
Expand All @@ -67,32 +69,34 @@ export function FieldPresenceInner({
stack = true
}: InnerProps) {
const uniquePresence = uniqBy(presence || [], item => item.user.id)
const sorted = sortBy(uniquePresence, presence => presence.lastActiveAt)
const sorted = sortBy(uniquePresence, _presence => _presence.lastActiveAt)
const [hidden, visible] = stack ? splitRight(sorted, maxAvatars) : [[], sorted]

const avatars = [
...visible.reverse().map(visible => ({
key: visible.user.id,
...visible.reverse().map(_visible => ({
key: _visible.user.id,
element: (
<UserAvatar animateArrowFrom={animateArrowFrom} position={position} user={visible.user} />
<UserAvatar
animateArrowFrom={animateArrowFrom}
position={position}
status="online"
user={_visible.user}
/>
)
})),
hidden.length >= 2
? {
key: 'counter',
element: <StackCounter count={hidden.length} />
element: <AvatarCounter count={hidden.length} />
}
: null
].filter(Boolean)

const minWidth = -AVATAR_DISTANCE + (AVATAR_SIZE + AVATAR_DISTANCE) * maxAvatars

return (
<div className={styles.root}>
<PopoverList
items={uniquePresence}
mode="tooltip"
placement="top-end"
renderItem={item => <PresenceListItem status="online" user={item.user} />}
>
<PresenceTooltip items={uniquePresence} placement="top-end">
<div className={styles.inner} style={{minWidth}}>
{avatars.map(
(av, i) =>
Expand All @@ -113,7 +117,7 @@ export function FieldPresenceInner({
)
)}
</div>
</PopoverList>
</PresenceTooltip>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as PathUtils from '@sanity/util/paths'
import React from 'react'
import {Context} from './context'
import {FormFieldPresenceContext} from './context'

function trimChildPath(path, childPath) {
return PathUtils.startsWith(path, childPath) ? PathUtils.trimLeft(path, childPath) : []
Expand All @@ -14,7 +14,7 @@ interface Props {

export function PresenceScope(props: Props) {
const {readOnly, path, children} = props
const contextPresence = React.useContext(Context)
const contextPresence = React.useContext(FormFieldPresenceContext)
const childPresence = readOnly
? []
: (contextPresence || [])
Expand All @@ -25,5 +25,10 @@ export function PresenceScope(props: Props) {
...presence,
path: trimChildPath(path, presence.path)
}))
return <Context.Provider value={childPresence}>{children}</Context.Provider>

return (
<FormFieldPresenceContext.Provider value={childPresence}>
{children}
</FormFieldPresenceContext.Provider>
)
}
23 changes: 23 additions & 0 deletions packages/@sanity/base/src/presence/PresenceTooltip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.root {
padding: 0.25em 0;
}

.item {
padding: 0.25em 0.5em;
display: flex;
align-items: center;
}

.avatarContainer {
/* */
}

.textContainer {
flex: 1;
min-width: 0;
white-space: nowrap;
margin-top: -1px;
margin-left: 0.5em;
font-size: 13px;
line-height: 16px;
}
40 changes: 40 additions & 0 deletions packages/@sanity/base/src/presence/PresenceTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint-disable react/require-default-props */

import {Tooltip, TooltipPlacement} from 'part:@sanity/components/tooltip'
import React from 'react'
import {UserAvatar} from '../components/UserAvatar'
import {FormFieldPresence} from './types'

import styles from './PresenceTooltip.css'

interface PresenceTooltipProps {
children?: JSX.Element
items: FormFieldPresence[]
placement?: TooltipPlacement
}

export function PresenceTooltip(props: PresenceTooltipProps) {
const {children, items, placement} = props

const content = (
<div className={styles.root}>
{items.map(item => (
<div className={styles.item} key={item.user.id}>
<div className={styles.avatarContainer}>
<UserAvatar user={item.user} status="online" />
</div>

<div className={styles.textContainer}>
<div className={styles.item__displayName}>{item.user.displayName}</div>
</div>
</div>
))}
</div>
)

return (
<Tooltip content={content as any} placement={placement}>
{children as any}
</Tooltip>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const DEFAULT_MAX_AVATARS_FIELDS = 3
export const AVATAR_DISTANCE = -5

// height/width of the avatars in pixels
export const AVATAR_SIZE = 21
export const AVATAR_SIZE = 23

export const AVATAR_ARROW_HEIGHT = 4

Expand Down
4 changes: 4 additions & 0 deletions packages/@sanity/base/src/presence/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from 'react'
import {FormFieldPresence} from './types'

export const FormFieldPresenceContext = React.createContext<FormFieldPresence[]>([])

0 comments on commit 4efefc4

Please sign in to comment.