Skip to content

Commit

Permalink
Fix preview rendering inside packages, refactor logic related to view…
Browse files Browse the repository at this point in the history
… modes (#2328)

* fix max update depth exceeded while rendering previews inside packages

* Preview/Display: support executing effects when preview data changes

* SelectDropdown: support passing props to the root element, export option type

* refactor ViewModes-related logic

* ResourceCache: support persistent resources

* utils/voila: determine voila availability (cached)

* rm utils/global

* remove fetch-mock package

* mock for utils/voila
  • Loading branch information
nl0 authored Sep 3, 2021
1 parent 60113c5 commit 093ced4
Show file tree
Hide file tree
Showing 17 changed files with 316 additions and 481 deletions.
14 changes: 10 additions & 4 deletions catalog/app/components/Preview/Display.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import * as AWS from 'utils/AWS'
import AsyncResult from 'utils/AsyncResult'
import * as Config from 'utils/Config'
import StyledLink from 'utils/StyledLink'
import pipeThru from 'utils/pipeThru'

import render from './render'
import { PreviewError } from './types'
Expand Down Expand Up @@ -42,11 +41,17 @@ export default function PreviewDisplay({
renderProgress = defaultProgress,
renderMessage = defaultMessage,
renderAction = defaultAction,
onData,
}) {
const cfg = Config.use()
const noDl = noDownload != null ? noDownload : cfg.noDownload
return pipeThru(data)(
AsyncResult.case({

React.useEffect(() => {
onData?.(data)
}, [data, onData])

return AsyncResult.case(
{
_: renderProgress,
Ok: R.pipe(render, renderContents),
Err: PreviewError.case({
Expand Down Expand Up @@ -133,7 +138,8 @@ export default function PreviewDisplay({
action: !!retry && renderAction({ label: 'Retry', onClick: retry }),
}),
}),
}),
},
data,
)
}

Expand Down
9 changes: 6 additions & 3 deletions catalog/app/components/SelectDropdown/SelectDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import cx from 'classnames'
import * as React from 'react'
import * as M from '@material-ui/core'

Expand All @@ -19,7 +20,7 @@ const useStyles = M.makeStyles((t) => ({
},
}))

interface ValueBase {
export interface ValueBase {
toString: () => string
valueOf: () => string | number | boolean
}
Expand All @@ -36,7 +37,9 @@ export default function SelectDropdown<Value extends ValueBase>({
onChange,
options,
value,
}: SelectDropdownProps<Value>) {
className,
...props
}: SelectDropdownProps<Value> & M.PaperProps) {
const classes = useStyles()

const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
Expand All @@ -57,7 +60,7 @@ export default function SelectDropdown<Value extends ValueBase>({
const aboveSm = M.useMediaQuery(t.breakpoints.up('sm'))

return (
<M.Paper className={classes.root}>
<M.Paper className={cx(className, classes.root)} {...props}>
<M.Button
className={classes.button}
onClick={handleOpen}
Expand Down
1 change: 1 addition & 0 deletions catalog/app/components/SelectDropdown/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default } from './SelectDropdown'
export * from './SelectDropdown'
54 changes: 18 additions & 36 deletions catalog/app/containers/Bucket/File.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import * as FileView from './FileView'
import Section from './Section'
import renderPreview from './renderPreview'
import * as requests from './requests'
import useViewModes from './viewModes'
import { useViewModes, viewModeToSelectOption } from './viewModes'

const getCrumbs = ({ bucket, path, urls }) =>
R.chain(
Expand Down Expand Up @@ -325,7 +325,7 @@ export default function File({
},
location,
}) {
const { version, mode: viewModeSlug } = parseSearch(location.search)
const { version, mode } = parseSearch(location.search)
const classes = useStyles()
const { urls } = NamedRoutes.use()
const history = useHistory()
Expand Down Expand Up @@ -382,9 +382,18 @@ export default function File({
}),
})

const viewModes = useViewModes(path, mode)

const onViewModeChange = React.useCallback(
(m) => {
history.push(urls.bucketFile(bucket, encodedPath, version, m.valueOf()))
},
[history, urls, bucket, encodedPath, version],
)

const handle = { bucket, key: path, version }

const withPreview = (callback, mode) =>
const withPreview = (callback) =>
requests.ObjectExistence.case({
Exists: (h) => {
if (h.deleted) {
Expand All @@ -393,39 +402,12 @@ export default function File({
if (h.archived) {
return callback(AsyncResult.Err(Preview.PreviewError.Archived({ handle })))
}
return mode
? Preview.load(R.assoc('mode', mode.key, handle), callback)
: Preview.load(handle, callback)
return Preview.load({ ...handle, mode: viewModes.mode }, callback)
},
DoesNotExist: () =>
callback(AsyncResult.Err(Preview.PreviewError.InvalidVersion({ handle }))),
})

const [previewResult, setPreviewResult] = React.useState(false)
const onRender = React.useCallback(
(result) => {
if (AsyncResult.Ok.is(result) && !previewResult) {
setPreviewResult(result)
return renderPreview(AsyncResult.Pending())
}
return renderPreview(result)
},
[previewResult, setPreviewResult],
)

const { registryUrl } = Config.use()
const viewModes = useViewModes(registryUrl, path, previewResult)
const viewMode = React.useMemo(
() => viewModes.find(({ key }) => key === viewModeSlug) || viewModes[0] || null,
[viewModes, viewModeSlug],
)
const onViewModeChange = React.useCallback(
(mode) => {
history.push(urls.bucketFile(bucket, encodedPath, version, mode.key))
},
[history, urls, bucket, encodedPath, version],
)

return (
<FileView.Root>
<MetaTitle>{[path || 'Files', bucket]}</MetaTitle>
Expand All @@ -449,11 +431,11 @@ export default function File({
</div>

<div className={classes.actions}>
{!!viewModes.length && (
<FileView.ViewWithVoilaButtonLayout
{!!viewModes.modes.length && (
<FileView.ViewModeSelector
className={classes.button}
modesList={viewModes}
mode={viewMode}
options={viewModes.modes.map(viewModeToSelectOption)}
value={viewModeToSelectOption(viewModes.mode)}
onChange={onViewModeChange}
/>
)}
Expand Down Expand Up @@ -487,7 +469,7 @@ export default function File({
Err: (e) => {
throw e
},
Ok: withPreview(onRender, viewMode),
Ok: withPreview(renderPreview(viewModes.handlePreviewResult)),
})}
</Section>
</>
Expand Down
13 changes: 3 additions & 10 deletions catalog/app/containers/Bucket/FileView.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,12 @@ export function DownloadButton({ className, handle }) {
))
}

const viewModeToSelectOption = ({ key, label }) => ({
key,
toString: () => label,
valueOf: () => key,
})

export function ViewWithVoilaButtonLayout({ modesList, mode, ...props }) {
export function ViewModeSelector({ className, ...props }) {
const classes = useDownloadButtonStyles()
const t = M.useTheme()
const sm = M.useMediaQuery(t.breakpoints.down('sm'))
const options = React.useMemo(() => modesList.map(viewModeToSelectOption), [modesList])
const value = React.useMemo(() => viewModeToSelectOption(mode), [mode])
return (
<SelectDropdown options={options} value={value} {...props}>
<SelectDropdown className={cx(classes.root, className)} {...props}>
{sm ? <M.Icon>visibility</M.Icon> : 'View as:'}
</SelectDropdown>
)
Expand Down
80 changes: 31 additions & 49 deletions catalog/app/containers/Bucket/PackageTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import Summary from './Summary'
import * as errors from './errors'
import renderPreview from './renderPreview'
import * as requests from './requests'
import useViewModes from './viewModes'
import { useViewModes, viewModeToSelectOption } from './viewModes'

/*
function ExposeLinkedData({ bucketCfg, bucket, name, hash, modified }) {
Expand Down Expand Up @@ -395,11 +395,12 @@ const useFileDisplayStyles = M.makeStyles((t) => ({
},
}))

function FileDisplay({ bucket, mode: modeSlug, name, hash, revision, path, crumbs }) {
function FileDisplay({ bucket, mode, name, hash, revision, path, crumbs }) {
const s3 = AWS.S3.use()
const credentials = AWS.Credentials.use()
const { apiGatewayEndpoint: endpoint, noDownload } = Config.use()

const history = useHistory()
const { urls } = NamedRoutes.use()
const classes = useFileDisplayStyles()

const data = useData(requests.packageFileDetail, {
Expand All @@ -412,6 +413,25 @@ function FileDisplay({ bucket, mode: modeSlug, name, hash, revision, path, crumb
path,
})

const viewModes = useViewModes(path, mode)

const onViewModeChange = React.useCallback(
(m) => {
history.push(urls.bucketPackageTree(bucket, name, revision, path, m.valueOf()))
},
[bucket, history, name, path, revision, urls],
)

const withPreview = ({ archived, deleted, handle }, callback) => {
if (deleted) {
return callback(AsyncResult.Err(Preview.PreviewError.Deleted({ handle })))
}
if (archived) {
return callback(AsyncResult.Err(Preview.PreviewError.Archived({ handle })))
}
return Preview.load({ ...handle, mode: viewModes.mode }, callback)
}

const renderProgress = () => (
// TODO: skeleton placeholder
<>
Expand All @@ -438,47 +458,6 @@ function FileDisplay({ bucket, mode: modeSlug, name, hash, revision, path, crumb
</>
)

const withPreview = ({ archived, deleted, handle, mode }, callback) => {
if (deleted) {
return callback(AsyncResult.Err(Preview.PreviewError.Deleted({ handle })))
}
if (archived) {
return callback(AsyncResult.Err(Preview.PreviewError.Archived({ handle })))
}
return mode
? Preview.load(R.assoc('mode', mode.key, handle), callback)
: Preview.load(handle, callback)
}

const history = useHistory()
const { urls } = NamedRoutes.use()

const [previewResult, setPreviewResult] = React.useState(false)
const onRender = React.useCallback(
(result) => {
if (previewResult === result) {
return renderPreview(result)
}

setPreviewResult(result)
return renderPreview(AsyncResult.Pending())
},
[previewResult, setPreviewResult],
)

const { registryUrl } = Config.use()
const viewModes = useViewModes(registryUrl, path, previewResult)
const viewMode = React.useMemo(
() => viewModes.find(({ key }) => key === modeSlug) || viewModes[0] || null,
[viewModes, modeSlug],
)
const onViewModeChange = React.useCallback(
(mode) => {
history.push(urls.bucketPackageTree(bucket, name, revision, path, mode.key))
},
[bucket, history, name, path, revision, urls],
)

return data.case({
Ok: ({ meta, ...handle }) => (
<Data fetch={requests.getObjectExistence} params={{ s3, ...handle }}>
Expand All @@ -496,11 +475,11 @@ function FileDisplay({ bucket, mode: modeSlug, name, hash, revision, path, crumb
Exists: ({ archived, deleted }) => (
<>
<TopBar crumbs={crumbs}>
{!!viewModes.length && (
<FileView.ViewWithVoilaButtonLayout
{!!viewModes.modes.length && (
<FileView.ViewModeSelector
className={classes.button}
modesList={viewModes}
mode={viewMode}
options={viewModes.modes.map(viewModeToSelectOption)}
value={viewModeToSelectOption(viewModes.mode)}
onChange={onViewModeChange}
/>
)}
Expand All @@ -511,7 +490,10 @@ function FileDisplay({ bucket, mode: modeSlug, name, hash, revision, path, crumb
<PkgCode {...{ bucket, name, hash, revision, path }} />
<FileView.Meta data={AsyncResult.Ok(meta)} />
<Section icon="remove_red_eye" heading="Preview" expandable={false}>
{withPreview({ archived, deleted, handle, mode: viewMode }, onRender)}
{withPreview(
{ archived, deleted, handle },
renderPreview(viewModes.handlePreviewResult),
)}
</Section>
</>
),
Expand Down
2 changes: 1 addition & 1 deletion catalog/app/containers/Bucket/renderPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ const renderProgress = () => (
</Message>
)

export default Preview.display({ renderMessage, renderProgress })
export default (onData) => Preview.display({ renderMessage, renderProgress, onData })
Loading

0 comments on commit 093ced4

Please sign in to comment.