Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow encoding draft ids #982

Merged
merged 3 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions packages/core-loader/src/live-mode/enableLiveMode.ts
stipsan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,13 @@ export function enableLiveMode(options: LazyEnableLiveModeOptions): () => void {
const key = JSON.stringify({ perspective, query, params })
const value = cache.get(key)
if (value) {
$fetch.setKey('data', value.result)
$fetch.setKey('sourceMap', value.resultSourceMap)
$fetch.setKey('perspective', perspective)
$fetch.setKey('loading', false)
$fetch.set({
data: value.result,
error: undefined,
loading: false,
perspective,
sourceMap: value.resultSourceMap,
})
documentsOnPage.push(...(value.resultSourceMap?.documents ?? []))
}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/core-loader/test/encodeDataAttribute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,14 +714,14 @@ describe('encodeDataAttribute', () => {

test('string paths', () => {
expect(encodeDataAttribute('page.sections[4].style')).toMatchInlineSnapshot(
`"id=home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
`"id=drafts.home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
)
})
test('array paths', () => {
expect(
encodeDataAttribute(['page', 'sections', 4, 'style']),
).toMatchInlineSnapshot(
`"id=home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
`"id=drafts.home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
)
})
})
Expand All @@ -735,20 +735,20 @@ describe('scoping', () => {
test('string paths', () => {
const scoped = encodeDataAttribute.scope('page.sections[4]')
expect(scoped('style')).toMatchInlineSnapshot(
`"id=home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
`"id=drafts.home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
)
})
test('array paths', () => {
const scoped = encodeDataAttribute.scope(['page', 'sections', 4])
expect(scoped(['style'])).toMatchInlineSnapshot(
`"id=home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
`"id=drafts.home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
)
})
test('array paths recursive', () => {
const scoped = encodeDataAttribute.scope(['page'])
const scopedDeeper = scoped.scope(['sections', 4])
expect(scopedDeeper(['style'])).toMatchInlineSnapshot(
`"id=home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
`"id=drafts.home;type=page;path=sections:0bd049fc047a.style;base=%2Fstudio"`,
)
})
})
8 changes: 8 additions & 0 deletions packages/presentation/src/PresentationTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,14 @@ export default function PresentationTool(props: {
[navigate],
)

// Dispatch a perspective message when the perspective changes
useEffect(() => {
channel?.send('overlays', 'presentation/perspective', {
perspective: state.perspective,
})
}, [channel, state.perspective])

// Dispatch a focus or blur message when the id or path change
useEffect(() => {
if (params.id && params.path) {
channel?.send('overlays', 'presentation/focus', {
Expand Down
2 changes: 2 additions & 0 deletions packages/presentation/src/loader/LoaderQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export default function LoaderQueries(props: LoaderQueriesProps): JSX.Element {
useEffect(() => {
if (channel) {
const { projectId, dataset } = clientConfig
// @todo - Can this be migrated/deprecated in favour of emitting
// `presentation/perspective` at a higher level?
stipsan marked this conversation as resolved.
Show resolved Hide resolved
channel.send('loaders', 'loader/perspective', {
projectId: projectId!,
dataset: dataset!,
Expand Down
2 changes: 1 addition & 1 deletion packages/presentation/src/useDocumentsOnPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function useDocumentsOnPage(
(
key: string,
perspective: ClientPerspective,
sourceDocuments: DocumentOnPage[],
sourceDocuments: DocumentOnPage[] = [],
) => {
const documents = sourceDocuments.filter((sourceDocument) => {
if ('_projectId' in sourceDocument && sourceDocument._projectId) {
Expand Down
4 changes: 4 additions & 0 deletions packages/presentation/src/useParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PresentationSearchParams,
PresentationStateParams,
} from './types'
import { getPublishedId } from 'sanity'

function pruneObject<T extends RouterState | PresentationParams>(obj: T): T {
return Object.fromEntries(
Expand Down Expand Up @@ -104,6 +105,9 @@ export function useParams({
() =>
debounce<PresentationNavigate>(
(nextState, nextSearchState = {}, forceReplace) => {
// Force navigation to use published IDs only
if (nextState.id) nextState.id = getPublishedId(nextState.id)

// Extract type, id and path as 'routerState'
const { _searchParams: routerSearchParams, ...routerState } =
routerStateRef.current
Expand Down
20 changes: 14 additions & 6 deletions packages/svelte-loader/src/defineUseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,20 @@ export function defineUseQuery({
onMount(() =>
$fetcher.subscribe((snapshot) => {
const prev = get($writeable)
if (
prev.error !== snapshot.error ||
prev.loading !== snapshot.loading ||
prev.perspective !== snapshot.perspective ||
!isEqual(prev.data, snapshot.data)
) {

if (prev.error !== snapshot.error) {
$writeable.set(snapshot)
}

if (prev.loading !== snapshot.loading) {
$writeable.set(snapshot)
}

if (prev.perspective !== snapshot.perspective) {
$writeable.set(snapshot)
}

if (!isEqual(prev.data, snapshot.data)) {
$writeable.set(snapshot)
}
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getPublishedId, studioPath } from '@sanity/client/csm'
import {
boolean,

Check failure on line 3 in packages/visual-editing-helpers/src/csm/transformSanityNodeData.ts

View workflow job for this annotation

GitHub Actions / build

'boolean' is defined but never used
is,
minLength,
object,
Expand All @@ -17,6 +18,8 @@

export type { SanityNode, SanityStegaNode }

export const DRAFTS_PREFIX = 'drafts.'

const lengthyStr = string([minLength(1)])
const optionalLengthyStr = optional(lengthyStr)

Expand All @@ -29,6 +32,7 @@
tool: optionalLengthyStr,
type: optionalLengthyStr,
workspace: optionalLengthyStr,
isDraft: optional(string()),
})

const sanityLegacyNodeSchema = object({
Expand Down Expand Up @@ -71,11 +75,17 @@
['base', encodeURIComponent(baseUrl)],
['workspace', workspace],
['tool', tool],
['isDraft', _id.startsWith(DRAFTS_PREFIX)],
]

return parts
.filter(([, value]) => !!value)
.map((part) => part.join('='))
.map((part) => {
const [key, value] = part
// For true values, just display the key
if (value === true) return key
return part.join('=')
})
.join(';')
}

Expand All @@ -89,7 +99,7 @@

const data = segments.reduce((acc, segment) => {
const [key, value] = segment.split('=')
if (!key || !value) return acc
if (!key || (segment.includes('=') && !value)) return acc

switch (key) {
case 'id':
Expand All @@ -116,6 +126,9 @@
case 'dataset':
acc.dataset = value
break
case 'isDraft':
acc.isDraft = ''
break
default:
}

Expand Down
13 changes: 10 additions & 3 deletions packages/visual-editing-helpers/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ export type QueryCacheKey = `${string}-${string}`
* @public
*/
export type SanityNode = {
projectId?: string
baseUrl: string
dataset?: string
id: string
isDraft?: string
path: string
type?: string
baseUrl: string
projectId?: string
tool?: string
type?: string
workspace?: string
}

Expand Down Expand Up @@ -121,6 +122,12 @@ export type PresentationMsg =
type: 'presentation/refresh'
data: HistoryRefresh
}
| {
type: 'presentation/perspective'
data: {
perspective: ClientPerspective
}
}

/**@public */
export interface VisualEditingPayloads {
Expand Down
13 changes: 6 additions & 7 deletions packages/visual-editing/src/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,14 @@ export function createOverlayController({
}

if (node instanceof HTMLElement) {
// @todo - We need to handle cases where `data-sanity` attributes may
// have changed, so it's not enough to ignore previously registered
// elements. We can just unregister and re-register elements instead of
// attempting to update their data. Can this be made more efficient?
if (elementsMap.has(node)) {
const sanityNodes = findSanityNodes({ childNodes: [node] })
// Check existing nodes are still valid
if (!sanityNodes.length) {
unregisterElement(node)
}
} else {
registerElements({ childNodes: [node] })
unregisterElement(node)
}
registerElements({ childNodes: [node] })
}

return true
Expand Down
71 changes: 48 additions & 23 deletions packages/visual-editing/src/ui/Overlays.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { ChannelStatus } from '@sanity/channels'
import type { ContentSourceMapDocuments } from '@sanity/client'
import type {
ClientPerspective,
ContentSourceMapDocuments,
} from '@sanity/client'
import {
isHTMLAnchorElement,
isHTMLElement,
Expand All @@ -11,6 +14,7 @@ import {
isHotkey,
type SanityNode,
} from '@sanity/visual-editing-helpers'
import { DRAFTS_PREFIX } from '@sanity/visual-editing-helpers/csm'
import {
type FunctionComponent,
useCallback,
Expand Down Expand Up @@ -77,14 +81,14 @@ export const Overlays: FunctionComponent<{

const [status, setStatus] = useState<ChannelStatus>()

const [{ elements, wasMaybeCollapsed }, dispatch] = useReducer(
const [{ elements, wasMaybeCollapsed, perspective }, dispatch] = useReducer(
overlayStateReducer,
undefined,
() => ({
{
elements: [],
focusPath: '',
wasMaybeCollapsed: false,
}),
perspective: 'published',
},
)
const [rootElement, setRootElement] = useState<HTMLElement | null>(null)
const [overlayEnabled, setOverlayEnabled] = useState(true)
Expand All @@ -96,6 +100,8 @@ export const Overlays: FunctionComponent<{
dispatch({ type, data })
} else if (type === 'presentation/blur') {
dispatch({ type, data })
} else if (type === 'presentation/perspective') {
dispatch({ type, data })
} else if (type === 'presentation/navigate') {
history?.update(data)
} else if (type === 'presentation/toggleOverlay') {
Expand All @@ -109,49 +115,68 @@ export const Overlays: FunctionComponent<{
}
}, [channel, history])

const nodeIdsRef = useRef<Set<string>>(new Set())
const lastReported = useRef<
| {
nodeIds: Set<string>
perspective: ClientPerspective
}
| undefined
>(undefined)

const reportDocuments = useCallback(
(documents: ContentSourceMapDocuments) => {
(documents: ContentSourceMapDocuments, perspective: ClientPerspective) => {
channel?.send('visual-editing/documents', {
perspective: 'previewDrafts',
documents,
perspective,
})
},
[channel],
)

useEffect(() => {
// Report only `SanityNode`, if a node is untransformed (`SanityStegaNode`)
// at this stage it will not contain the necessary document data
// Report only nodes of type `SanityNode`. Untransformed `SanityStegaNode`
// nodes without an `id`, are not reported as they will not contain the
// necessary document data.
const nodes = elements
.map((e) => e.sanity)
.filter((s) => 'id' in s) as SanityNode[]
.map((e) => {
const { sanity } = e
if (!('id' in sanity)) return null
return {
...sanity,
id: 'isDraft' in sanity ? `${DRAFTS_PREFIX}${sanity.id}` : sanity.id,
}
})
.filter((s) => !!s) as SanityNode[]

const nodeIds = new Set<string>(nodes.map((e) => e.id))
// Only report documents if some document IDs have changed
if (!isEqualSets(nodeIds, nodeIdsRef.current)) {
// Report if:
// - Documents not yet reported
// - Document IDs changed
// - Perspective changed
if (
!lastReported.current ||
!isEqualSets(nodeIds, lastReported.current.nodeIds) ||
perspective !== lastReported.current.perspective
) {
const documentsOnPage: ContentSourceMapDocuments = Array.from(
nodeIds,
).map((_id) => {
const {
type,
projectId: _projectId,
dataset: _dataset,
} = nodes.find((node) => node.id === _id)!
const node = nodes.find((node) => node.id === _id)!
const { type, projectId: _projectId, dataset: _dataset } = node
return _projectId && _dataset
? { _id, _type: type!, _projectId, _dataset }
: { _id, _type: type! }
})
nodeIdsRef.current = nodeIds
reportDocuments(documentsOnPage)
lastReported.current = { nodeIds, perspective }
reportDocuments(documentsOnPage, perspective)
}
}, [elements, reportDocuments])
}, [elements, perspective, reportDocuments])

const overlayEventHandler: OverlayEventHandler = useCallback(
(message) => {
if (message.type === 'element/click') {
channel.send('overlay/focus', message.sanity)
const { sanity } = message
channel.send('overlay/focus', sanity)
} else if (message.type === 'overlay/activate') {
channel.send('overlay/toggle', { enabled: true })
} else if (message.type === 'overlay/deactivate') {
Expand Down
Loading
Loading