Skip to content

Commit

Permalink
Configurable gallery visibility (#3421)
Browse files Browse the repository at this point in the history
  • Loading branch information
fiskus authored May 10, 2023
1 parent d1df293 commit e206111
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 247 deletions.
127 changes: 127 additions & 0 deletions catalog/app/containers/Bucket/Gallery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { basename } from 'path'

import * as React from 'react'
import * as R from 'ramda'
import * as RRDom from 'react-router-dom'
import * as M from '@material-ui/core'

import * as Pagination from 'components/Pagination'
import Thumbnail from 'components/Thumbnail'
import Skel from 'components/Skeleton'
import AsyncResult from 'utils/AsyncResult'
import * as LogicalKeyResolver from 'utils/LogicalKeyResolver'
import * as NamedRoutes from 'utils/NamedRoutes'

import * as Summarize from './Summarize'

const useImageGridStyles = M.makeStyles((t) => ({
root: {
display: 'grid',
gridAutoRows: 'max-content',
gridColumnGap: t.spacing(2),
gridRowGap: t.spacing(2),
gridTemplateColumns: '1fr',
[t.breakpoints.up('sm')]: {
gridTemplateColumns: '1fr 1fr 1fr',
},
[t.breakpoints.up('md')]: {
gridTemplateColumns: '1fr 1fr 1fr 1fr',
},
[t.breakpoints.up('lg')]: {
gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr',
},
},
}))

function ImageGrid({ children }: React.PropsWithChildren<{}>) {
const classes = useImageGridStyles()
return <div className={classes.root}>{children}</div>
}

const useThumbnailsStyles = M.makeStyles({
link: {
overflow: 'hidden',
},
img: {
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
maxWidth: '100%',
},
})

interface ThumbnailsProps {
images: LogicalKeyResolver.S3SummarizeHandle[]
mkUrl?: Summarize.MakeURL
}

export function Thumbnails({ images, mkUrl }: ThumbnailsProps) {
const classes = useThumbnailsStyles()
const { urls } = NamedRoutes.use()

const scrollRef = React.useRef<HTMLDivElement | null>(null)
const scroll = React.useCallback(
(prev) => {
if (prev && scrollRef.current) scrollRef.current.scrollIntoView()
},
[scrollRef],
)

const pagination = Pagination.use(images, { perPage: 25, onChange: scroll })

return (
<Summarize.Section
heading={
<>
Images ({pagination.from}&ndash;{Math.min(pagination.to, images.length)} of{' '}
{images.length})
</>
}
footer={pagination.pages > 1 && <Pagination.Controls {...pagination} />}
>
<div ref={scrollRef} />
<ImageGrid>
{pagination.paginated.map((i: LogicalKeyResolver.S3SummarizeHandle) => (
<RRDom.Link
key={i.logicalKey || i.key}
to={
mkUrl ? mkUrl(i) : urls.bucketFile(i.bucket, i.key, { version: i.version })
}
className={classes.link}
>
{/* @ts-expect-error */}
<Summarize.HandleResolver handle={i}>
{AsyncResult.case({
_: () => null,
Ok: (resolved: LogicalKeyResolver.S3SummarizeHandle) => (
// @ts-expect-error
<Thumbnail
handle={resolved}
className={classes.img}
alt={basename(i.logicalKey || i.key)}
title={basename(i.logicalKey || i.key)}
/>
),
})}
</Summarize.HandleResolver>
</RRDom.Link>
))}
</ImageGrid>
</Summarize.Section>
)
}

export function Skeleton() {
return (
<Summarize.Section key="thumbs:skel" heading={<Summarize.HeadingSkel />}>
<ImageGrid>
{R.times(
(i) => (
<Skel key={`thumbs:skel:${i}`} height={200} />
),
9,
)}
</ImageGrid>
</Summarize.Section>
)
}
2 changes: 1 addition & 1 deletion catalog/app/containers/Bucket/Meta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export function PackageMeta({ data, ...props }: WrapperProps) {
return BucketPreferences.Result.match(
{
Ok: ({ ui: { blocks } }) =>
!!blocks.meta && (
blocks.meta && (
<PackageMetaSection meta={meta} preferences={blocks.meta} {...props} />
),
_: noop,
Expand Down
132 changes: 41 additions & 91 deletions catalog/app/containers/Bucket/Overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,22 @@ import * as M from '@material-ui/core'
import { fade } from '@material-ui/core/styles'
import useComponentSize from '@rehooks/component-size'

import * as Pagination from 'components/Pagination'
import Skeleton from 'components/Skeleton'
import StackedAreaChart from 'components/StackedAreaChart'
import Thumbnail from 'components/Thumbnail'
import cfg from 'constants/config'
import * as authSelectors from 'containers/Auth/selectors'
import * as APIConnector from 'utils/APIConnector'
import * as AWS from 'utils/AWS'
import AsyncResult from 'utils/AsyncResult'
import * as BucketPreferences from 'utils/BucketPreferences'
import Data, { useData } from 'utils/Data'
import { useQueryS } from 'utils/GraphQL'
import * as LinkedData from 'utils/LinkedData'
import * as NamedRoutes from 'utils/NamedRoutes'
import * as SVG from 'utils/SVG'
import Link from 'utils/StyledLink'
import { readableBytes, readableQuantity, formatQuantity } from 'utils/string'

import * as Gallery from './Gallery'
import * as Summarize from './Summarize'
import * as requests from './requests'
import BUCKET_CONFIG_QUERY from './OverviewBucketConfig.generated'
Expand Down Expand Up @@ -851,79 +850,6 @@ function Head({ s3, overviewUrl, bucket, description }) {
)
}

const ImageGrid = M.styled(M.Box)(({ theme: t }) => ({
display: 'grid',
gridAutoRows: 'max-content',
gridColumnGap: t.spacing(2),
gridRowGap: t.spacing(2),
gridTemplateColumns: '1fr',
[t.breakpoints.up('sm')]: {
gridTemplateColumns: '1fr 1fr 1fr',
},
[t.breakpoints.up('md')]: {
gridTemplateColumns: '1fr 1fr 1fr 1fr',
},
[t.breakpoints.up('lg')]: {
gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr',
},
}))

const useThumbnailsStyles = M.makeStyles({
link: {
overflow: 'hidden',
},
img: {
display: 'block',
marginLeft: 'auto',
marginRight: 'auto',
maxWidth: '100%',
},
})

function Thumbnails({ images }) {
const classes = useThumbnailsStyles()
const { urls } = NamedRoutes.use()

const scrollRef = React.useRef(null)
const scroll = React.useCallback(
(prev) => {
if (prev && scrollRef.current) scrollRef.current.scrollIntoView()
},
[scrollRef],
)

const pagination = Pagination.use(images, { perPage: 25, onChange: scroll })

return (
<Summarize.Section
heading={
<>
Images ({pagination.from}&ndash;{Math.min(pagination.to, images.length)} of{' '}
{images.length})
</>
}
>
<div ref={scrollRef} />
<ImageGrid>
{pagination.paginated.map((i) => (
<Link
key={i.key}
to={urls.bucketFile(i.bucket, i.key, { version: i.version })}
className={classes.link}
>
<Thumbnail handle={i} className={classes.img} alt={i.key} title={i.key} />
</Link>
))}
</ImageGrid>
{pagination.pages > 1 && (
<M.Box display="flex" justifyContent="flex-end" pt={2}>
<Pagination.Controls {...pagination} />
</M.Box>
)}
</Summarize.Section>
)
}

function Readmes({ s3, overviewUrl, bucket }) {
return (
<Data fetch={requests.bucketReadmes} params={{ s3, overviewUrl, bucket }}>
Expand Down Expand Up @@ -959,20 +885,32 @@ function Imgs({ s3, overviewUrl, inStack, bucket }) {
return (
<Data fetch={requests.bucketImgs} params={{ req, s3, overviewUrl, inStack, bucket }}>
{AsyncResult.case({
Ok: (images) => (images.length ? <Thumbnails images={images} /> : null),
_: () => (
<Summarize.Section key="thumbs:skel" heading={<Summarize.HeadingSkel />}>
<ImageGrid>
{R.times(
(i) => (
// eslint-disable-next-line react/no-array-index-key
<Skeleton key={i} height={200} />
),
9,
)}
</ImageGrid>
</Summarize.Section>
),
Ok: (images) => (images.length ? <Gallery.Thumbnails images={images} /> : null),
_: () => <Gallery.Skeleton />,
})}
</Data>
)
}

function ThumbnailsWrapper({
s3,
overviewUrl,
inStack,
bucket,
preferences: galleryPrefs,
}) {
if (cfg.noOverviewImages || !galleryPrefs) return null
if (!galleryPrefs.overview) return null
return (
<Data fetch={requests.ensureQuiltSummarizeIsPresent} params={{ s3, bucket }}>
{AsyncResult.case({
Ok: (h) =>
(!h || galleryPrefs.summarize) && (
<Imgs {...{ s3, bucket, inStack, overviewUrl }} />
),
Err: () => <Imgs {...{ s3, bucket, inStack, overviewUrl }} />,
Pending: () => <Gallery.Skeleton />,
_: () => null,
})}
</Data>
)
Expand All @@ -988,6 +926,7 @@ export default function Overview({
const inStack = !!bucketConfig
const overviewUrl = bucketConfig?.overviewUrl
const description = bucketConfig?.description
const prefs = BucketPreferences.use()
return (
<M.Box pb={{ xs: 0, sm: 4 }} mx={{ xs: -2, sm: 0 }} position="relative" zIndex={1}>
{inStack && (
Expand All @@ -1008,7 +947,18 @@ export default function Overview({
</M.Box>
)}
<Readmes {...{ s3, bucket, overviewUrl }} />
{!cfg.noOverviewImages && <Imgs {...{ s3, bucket, inStack, overviewUrl }} />}
{BucketPreferences.Result.match(
{
Ok: ({ ui: { blocks } }) => (
<ThumbnailsWrapper
{...{ s3, bucket, inStack, overviewUrl, preferences: blocks.gallery }}
/>
),
Pending: () => <Gallery.Skeleton />,
Init: R.F,
},
prefs,
)}
<Summarize.SummaryRoot {...{ s3, bucket, inStack, overviewUrl }} />
</M.Box>
)
Expand Down
Loading

0 comments on commit e206111

Please sign in to comment.