Skip to content

Commit

Permalink
[desk-tool] Add features context with useDeskToolFeatures hook
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent 88b7c65 commit 6cf9740
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 42 deletions.
1 change: 1 addition & 0 deletions packages/@sanity/desk-tool/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const BREAKPOINT_SCREEN_MEDIUM = 512
export const EMPTY_PARAMS = {}
export const LOADING_PANE = Symbol('LOADING_PANE')
7 changes: 7 additions & 0 deletions packages/@sanity/desk-tool/src/features/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {createContext} from 'react'
import {DeskToolFeatures} from './types'

export const DeskToolFeaturesContext = createContext<DeskToolFeatures>({
reviewChanges: false,
splitViews: false
})
28 changes: 28 additions & 0 deletions packages/@sanity/desk-tool/src/features/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {combineLatest, Observable} from 'rxjs'
import {map} from 'rxjs/operators'
import {BREAKPOINT_SCREEN_MEDIUM} from '../constants'
import windowWidth$ from '../utils/windowWidth'
import {DeskToolFeatures} from './types'

export function createDeskToolFeaturesController() {
// determine if the screen is narrow
const isNarrowScreen$: Observable<boolean> = windowWidth$.pipe(
map((windowWidth: number) => windowWidth < BREAKPOINT_SCREEN_MEDIUM)
)

// determine if "reviewChanges" are available
const reviewChanges$ = isNarrowScreen$.pipe(map(val => !val))

// determine if "splitViews" are available
const splitViews$ = isNarrowScreen$.pipe(map(val => !val))

// combine streams of features flags
const state$: Observable<DeskToolFeatures> = combineLatest([reviewChanges$, splitViews$]).pipe(
map(([reviewChanges, splitViews]) => ({
reviewChanges,
splitViews
}))
)

return {state$}
}
12 changes: 12 additions & 0 deletions packages/@sanity/desk-tool/src/features/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {useContext} from 'react'
import {DeskToolFeaturesContext} from './context'

export function useDeskToolFeatures() {
const features = useContext(DeskToolFeaturesContext)

if (!features) {
throw new Error('DeskTool: missing features in context')
}

return features
}
3 changes: 3 additions & 0 deletions packages/@sanity/desk-tool/src/features/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './provider'
export * from './hooks'
export * from './types'
18 changes: 18 additions & 0 deletions packages/@sanity/desk-tool/src/features/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, {useEffect, useMemo, useState} from 'react'
import {DeskToolFeaturesContext} from './context'
import {createDeskToolFeaturesController} from './controller'
import {DeskToolFeatures} from './types'

export function DeskToolFeaturesProvider({children}: {children: React.ReactNode}) {
const controller = useMemo(() => createDeskToolFeaturesController(), [])
const [state, setState] = useState<DeskToolFeatures>({reviewChanges: false, splitViews: false})

useEffect(() => {
const sub = controller.state$.subscribe(setState)
return () => sub.unsubscribe()
}, [controller])

return (
<DeskToolFeaturesContext.Provider value={state}>{children}</DeskToolFeaturesContext.Provider>
)
}
4 changes: 4 additions & 0 deletions packages/@sanity/desk-tool/src/features/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DeskToolFeatures {
reviewChanges: boolean
splitViews: boolean
}
40 changes: 23 additions & 17 deletions packages/@sanity/desk-tool/src/panes/documentPane/documentPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as PathUtils from '@sanity/util/paths'
import classNames from 'classnames'
import React, {useCallback} from 'react'
import {usePaneRouter} from '../../contexts/PaneRouterContext'
import {useDeskToolFeatures} from '../../features'
import {ChangesPanel} from './changesPanel'
import {useDocumentHistory} from './documentHistory'
import {DocumentPanel, getProductionPreviewItem} from './documentPanel'
Expand Down Expand Up @@ -55,6 +56,7 @@ export function DocumentPane(props: DocumentPaneProps) {
views = []
} = props

const features = useDeskToolFeatures()
const {startTime} = useDocumentHistory()
const [showValidationTooltip, setShowValidationTooltip] = React.useState<boolean>(false)
const paneRouter = usePaneRouter()
Expand All @@ -65,26 +67,30 @@ export function DocumentPane(props: DocumentPaneProps) {
const isInspectOpen = paneRouter.params.inspect === 'on'
const isHistoryOpen = Boolean(startTime)

const handleKeyUp = useCallback((event: any) => {
if (event.key === 'Escape' && showValidationTooltip) {
setShowValidationTooltip(false)
}
const handleKeyUp = useCallback(
(event: any) => {
if (event.key === 'Escape' && showValidationTooltip) {
setShowValidationTooltip(false)
}

if (isInspectHotkey(event)) {
toggleInspect()
}
if (isInspectHotkey(event)) {
toggleInspect()
}

if (isPreviewHotkey(event)) {
const item = getProductionPreviewItem({
value,
rev: null
})
if (isPreviewHotkey(event)) {
const item = getProductionPreviewItem({
features,
value,
rev: null
})

if (item && item.url) {
window.open(item.url)
if (item && item.url) {
window.open(item.url)
}
}
}
}, [])
},
[features]
)

const toggleInspect = useCallback(
(toggle = !isInspectOpen) => {
Expand Down Expand Up @@ -153,7 +159,7 @@ export function DocumentPane(props: DocumentPaneProps) {
/>
</div>

{!isCollapsed && isHistoryOpen && (
{features.reviewChanges && !isCollapsed && isHistoryOpen && (
<div className={styles.changesContainer}>
<ChangesPanel documentId={documentId} schemaType={schemaType} />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames'
import {PortalProvider} from 'part:@sanity/components/portal'
import Snackbar from 'part:@sanity/components/snackbar/default'
import React, {createElement, useCallback, useEffect, useMemo, useRef} from 'react'
import {useDeskToolFeatures} from '../../../features'
import {useDocumentHistory} from '../documentHistory'
import {Doc, DocumentView, MenuItemGroup} from '../types'
import {DocumentOperationResults} from './documentOperationResults'
Expand Down Expand Up @@ -40,6 +41,7 @@ interface DocumentPanelProps {
}

export function DocumentPanel(props: DocumentPanelProps) {
const features = useDeskToolFeatures()
const portalContainerRef = useRef<HTMLDivElement | null>(null)
const portalRef = useRef(document.createElement('div'))
const {displayed, historyDisplayed, startTime, toggleHistory} = useDocumentHistory()
Expand All @@ -52,6 +54,7 @@ export function DocumentPanel(props: DocumentPanelProps) {
return (
getMenuItems({
canShowHistoryList: true,
features,
isHistoryEnabled: true,
isHistoryOpen: props.isHistoryOpen,
isLiveEditEnabled: props.schemaType.liveEdit === true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ClickOutside} from 'part:@sanity/components/click-outside'
import {Popover} from 'part:@sanity/components/popover'
import LanguageFilter from 'part:@sanity/desk-tool/language-select-component?'
import React, {useCallback, useState} from 'react'
import {useDeskToolFeatures} from '../../../../features'
import {DocumentView, MenuAction, MenuItemGroup} from '../../types'
import {DocumentPanelContextMenu} from './contextMenu'
import {DocumentHeaderTabs} from './tabs'
Expand Down Expand Up @@ -38,7 +39,9 @@ export interface DocumentPanelHeaderProps {
const isActionButton = (item: MenuAction) => (item as any).showAsAction
const isMenuButton = negate(isActionButton)

// eslint-disable-next-line complexity
export function DocumentPanelHeader(props: DocumentPanelHeaderProps) {
const features = useDeskToolFeatures()
const contextMenuItems = props.menuItems.filter(isMenuButton)
const [isContextMenuOpen, setContextMenuOpen] = useState(false)
const [isVersionSelectOpen, setVersionSelectOpen] = useState(false)
Expand Down Expand Up @@ -112,7 +115,7 @@ export function DocumentPanelHeader(props: DocumentPanelHeaderProps) {
</div>

<div className={styles.viewNav}>
{props.views.length > 1 && (
{features.splitViews && props.views.length > 1 && (
<div className={styles.tabsContainer}>
<DocumentHeaderTabs
activeViewId={props.activeViewId}
Expand Down Expand Up @@ -150,23 +153,25 @@ export function DocumentPanelHeader(props: DocumentPanelHeaderProps) {
</Popover>
</div>

<div className={styles.viewActions}>
{props.onSplitPane && props.views.length > 1 && (
<button type="button" onClick={props.onSplitPane} title="Split pane right">
<div tabIndex={-1}>
<SplitHorizontalIcon />
</div>
</button>
)}

{props.onSplitPane && props.isClosable && (
<button type="button" onClick={props.onCloseView} title="Close pane">
<div tabIndex={-1}>
<CloseIcon />
</div>
</button>
)}
</div>
{features.splitViews && (
<div className={styles.viewActions}>
{props.onSplitPane && props.views.length > 1 && (
<button type="button" onClick={props.onSplitPane} title="Split pane right">
<div tabIndex={-1}>
<SplitHorizontalIcon />
</div>
</button>
)}

{props.onSplitPane && props.isClosable && (
<button type="button" onClick={props.onCloseView} title="Close pane">
<div tabIndex={-1}>
<CloseIcon />
</div>
</button>
)}
</div>
)}
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import PublicIcon from 'part:@sanity/base/public-icon'
import HistoryIcon from 'part:@sanity/base/history-icon'
import Hotkeys from 'part:@sanity/components/typography/hotkeys'
import resolveProductionPreviewUrl from 'part:@sanity/transitional/production-preview/resolve-production-url?'
import {DeskToolFeatures} from '../../../../features'
import {Doc, MenuAction} from '../../types'

import styles from './menuItems.css'

interface Params {
canShowHistoryList?: boolean
features: DeskToolFeatures
isHistoryOpen?: boolean
isHistoryEnabled?: boolean
isLiveEditEnabled?: boolean
Expand All @@ -22,7 +24,14 @@ interface Params {
}

const getHistoryMenuItem = (params: Params): MenuAction | null => {
const {value, isLiveEditEnabled, isHistoryEnabled, isHistoryOpen, canShowHistoryList} = params
const {
features,
value,
isLiveEditEnabled,
isHistoryEnabled,
isHistoryOpen,
canShowHistoryList
} = params

if (isLiveEditEnabled || !canShowHistoryList) {
return null
Expand All @@ -33,7 +42,7 @@ const getHistoryMenuItem = (params: Params): MenuAction | null => {
action: 'browseHistory',
title: 'Browse history',
icon: HistoryIcon,
isDisabled: isHistoryOpen || !value
isDisabled: !features.reviewChanges || isHistoryOpen || !value
}
}

Expand Down
15 changes: 12 additions & 3 deletions packages/@sanity/desk-tool/src/tool/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {route, useRouterState} from 'part:@sanity/base/router'
import {parsePanesSegment, encodePanesSegment} from '../utils/parsePanesSegment'
import IntentResolver from '../components/IntentResolver'
import {EMPTY_PARAMS} from '../constants'
import DeskTool from './DeskTool'
import {DeskToolFeaturesProvider} from '../features'
import DeskToolRoot from './DeskTool'

function toState(pathSegment) {
return parsePanesSegment(decodeURIComponent(pathSegment))
Expand Down Expand Up @@ -48,7 +49,7 @@ function DeskToolPaneStateSyncer(props) {
return intent ? (
<IntentResolver intent={intent} params={params} payload={payload} />
) : (
<DeskTool {...props} onPaneChange={setActivePanes} />
<DeskToolRoot {...props} onPaneChange={setActivePanes} />
)
}

Expand Down Expand Up @@ -102,6 +103,14 @@ function MasterDetailIcon() {
)
}

function DeskTool() {
return (
<DeskToolFeaturesProvider>
<DeskToolPaneStateSyncer />
</DeskToolFeaturesProvider>
)
}

export default {
router: route('/', [
// "Asynchronous intent resolving" route
Expand Down Expand Up @@ -136,5 +145,5 @@ export default {
title: 'Desk',
name: 'desk',
icon: MasterDetailIcon,
component: DeskToolPaneStateSyncer
component: DeskTool
}
5 changes: 3 additions & 2 deletions packages/@sanity/desk-tool/src/utils/windowWidth.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Observable, merge} from 'rxjs'
import {map, share, debounceTime} from 'rxjs/operators'
import {map, shareReplay, debounceTime, startWith} from 'rxjs/operators'

const fromWindowEvent = eventName =>
new Observable(subscriber => {
Expand All @@ -16,7 +16,8 @@ const resize$ = fromWindowEvent('resize')
const windowWidth$ = merge(orientationChange$, resize$).pipe(
debounceTime(50),
map(() => window.innerWidth),
share()
shareReplay(1),
startWith(window.innerWidth)
)

export default windowWidth$

0 comments on commit 6cf9740

Please sign in to comment.