Skip to content

Commit 92584ab

Browse files
committed
add story carousel
1 parent 5a6c56e commit 92584ab

File tree

9 files changed

+253
-98
lines changed

9 files changed

+253
-98
lines changed

databox/client/src/api/asset.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import {AxiosRequestConfig} from 'axios';
1616
import {TFacets} from '../components/Media/Asset/Facets';
1717
import {AttributeBatchAction, AttributeBatchActionEnum} from './types.ts';
18+
import {SortWay} from './common.ts';
1819

1920
export interface GetAssetOptions {
2021
url?: string;
@@ -23,7 +24,7 @@ export interface GetAssetOptions {
2324
ids?: string[];
2425
parents?: string[];
2526
conditions?: string[];
26-
order?: Record<string, 'asc' | 'desc'>;
27+
order?: Record<string, SortWay>;
2728
group?: string[] | undefined;
2829
context?:
2930
| {

databox/client/src/api/attributeEntity.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import apiClient from './api-client';
22
import {AttributeEntity} from '../types';
33
import {ApiCollectionResponse, getHydraCollection} from './hydra';
4+
import {SortWay} from './common.ts';
45

56
const attributeEntityNS = '/attribute-entities';
67

@@ -15,7 +16,7 @@ export async function getAttributeEntities(
1516
const res = await apiClient.get(attributeEntityNS, {
1617
params: {
1718
...options,
18-
[`order[value]`]: 'asc',
19+
[`order[value]`]: SortWay.ASC,
1920
},
2021
});
2122

databox/client/src/api/common.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum SortWay {
2+
ASC = 'asc',
3+
DESC = 'desc',
4+
}

databox/client/src/api/entityList.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import apiClient from './api-client';
22
import {EntityList} from '../types';
33
import {ApiCollectionResponse, getHydraCollection} from './hydra';
4+
import {SortWay} from './common.ts';
45

56
export const entityTypeNS = '/entity-lists';
67

@@ -17,7 +18,7 @@ export async function getEntityLists(
1718
params: {
1819
...(options ?? {}),
1920
workspace: workspaceId,
20-
[`order[value]`]: 'asc',
21+
[`order[value]`]: SortWay.ASC,
2122
},
2223
});
2324

databox/client/src/components/Media/Asset/AssetThumb.tsx

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {DOMAttributes, ReactNode} from 'react';
1+
import React, {HTMLAttributes, ReactNode} from 'react';
22
import {Asset} from '../../../types';
33
import AssetFileIcon from './AssetFileIcon';
44
import assetClasses from '../../AssetList/classes';
@@ -11,7 +11,7 @@ import StoryChip from '../../AssetList/Layouts/StoryChip.tsx';
1111

1212
type Props = {
1313
asset: Asset;
14-
} & DOMAttributes<HTMLDivElement>;
14+
} & HTMLAttributes<HTMLDivElement>;
1515

1616
function AssetThumb({
1717
asset: {
@@ -52,9 +52,12 @@ function AssetThumb({
5252
return (
5353
<div
5454
{...domAttrs}
55-
className={classNames({
56-
[assetClasses.thumbWrapper]: true,
57-
})}
55+
className={classNames(
56+
{
57+
[assetClasses.thumbWrapper]: true,
58+
},
59+
[domAttrs.className]
60+
)}
5861
>
5962
{thumb ? (
6063
<div
@@ -126,21 +129,13 @@ export const thumbSx = (
126129
display: 'contents',
127130
},
128131
[`.${assetClasses.storyChip}`]: {
129-
'position': 'absolute',
130-
'width': '100%',
131-
'bottom': theme.spacing(1),
132-
'textAlign': 'center',
133-
'left': 0,
134-
'zIndex': 2,
135-
'display': 'inline-block',
136-
'.MuiChip-label': {
137-
width: 0,
138-
},
139-
'&:hover': {
140-
'.MuiChip-label': {
141-
width: 'auto',
142-
},
143-
},
132+
position: 'absolute',
133+
width: '100%',
134+
bottom: theme.spacing(1),
135+
textAlign: 'center',
136+
left: 0,
137+
zIndex: 2,
138+
display: 'inline-block',
144139
},
145140

146141
...createThumbActiveStyle(),

databox/client/src/components/Media/Asset/View/AssetView.tsx

Lines changed: 120 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {StackedModalProps, useParams} from '@alchemy/navigation';
77
import {Dimensions, filePlayerRelativeWrapperClassName} from '../Players';
88
import {Box, Typography} from '@mui/material';
99
import FileIntegrations from '../FileIntegrations.tsx';
10-
import {getAsset} from '../../../../api/asset.ts';
10+
import {getAsset, getAssets} from '../../../../api/asset.ts';
1111
import FullPageLoader from '../../../Ui/FullPageLoader.tsx';
1212
import RouteDialog from '../../../Dialog/RouteDialog.tsx';
1313
import {getAssetRenditions} from '../../../../api/rendition.ts';
@@ -31,6 +31,8 @@ import {
3131
} from '../../../Discussion/discussion.ts';
3232
import {useBindAnnotationMessage} from './useBindAnnotationMessage.ts';
3333
import AssetViewInfo from '../AssetViewInfo.tsx';
34+
import {ApiCollectionResponse} from '../../../../api/hydra.ts';
35+
import StoryCarousel, {storyCarouselHeight} from './StoryCarousel.tsx';
3436

3537
export type IntegrationOverlayCommonProps = {
3638
dimensions: Dimensions;
@@ -53,6 +55,7 @@ type Props = {} & StackedModalProps;
5355
export default function AssetView({modalIndex, open}: Props) {
5456
const menuWidth = 400;
5557
const headerHeight = 60;
58+
let heightRest = headerHeight;
5659
const {id: assetId, renditionId} = useParams();
5760
const assetAnnotationsRef: AssetAnnotationRef = useRef(null);
5861
const messageFormRef: MessageFormRef = useRef(null);
@@ -62,6 +65,13 @@ export default function AssetView({modalIndex, open}: Props) {
6265
>();
6366
const {t} = useTranslation();
6467
const queryKey = ['assets', assetId];
68+
const [storyAssets, setStoryAssets] =
69+
React.useState<ApiCollectionResponse<Asset>>();
70+
const [currentStoryAsset, setCurrentStoryAsset] = React.useState<
71+
Asset | undefined
72+
>();
73+
74+
console.log('currentStoryAsset', currentStoryAsset);
6575

6676
useChannelRegistration(`asset-${assetId}`, `asset_ingested`, () => {
6777
queryClient.invalidateQueries({queryKey});
@@ -97,13 +107,6 @@ export default function AssetView({modalIndex, open}: Props) {
97107
[setIntegrationOverlay]
98108
);
99109

100-
const dimensions = useMemo<Dimensions>(() => {
101-
return {
102-
width: winSize.innerWidth - menuWidth - scrollbarWidth,
103-
height: winSize.innerHeight - headerHeight - 2,
104-
};
105-
}, [winSize]);
106-
107110
const [[asset, renditions], rendition] = (
108111
isSuccess
109112
? [
@@ -127,6 +130,28 @@ export default function AssetView({modalIndex, open}: Props) {
127130
}
128131
}, [data, previousData, renditionId]);
129132

133+
const isStory = Boolean(asset?.storyCollection);
134+
if (isStory) {
135+
heightRest += storyCarouselHeight;
136+
}
137+
138+
const dimensions = useMemo<Dimensions>(() => {
139+
return {
140+
width: winSize.innerWidth - menuWidth - scrollbarWidth,
141+
height: winSize.innerHeight - heightRest - 2,
142+
};
143+
}, [winSize]);
144+
145+
React.useEffect(() => {
146+
if (asset?.storyCollection) {
147+
getAssets({
148+
parents: [asset.storyCollection.id],
149+
}).then(setStoryAssets);
150+
} else {
151+
setStoryAssets(undefined);
152+
}
153+
}, [asset]);
154+
130155
const {
131156
onNewAnnotation,
132157
onUpdateAnnotation,
@@ -148,6 +173,9 @@ export default function AssetView({modalIndex, open}: Props) {
148173
return <FullPageLoader />;
149174
}
150175

176+
const panelHeight = winSize.innerHeight - headerHeight;
177+
const displayedAsset = currentStoryAsset || asset;
178+
151179
return (
152180
<RouteDialog>
153181
{({onClose}) => (
@@ -171,6 +199,7 @@ export default function AssetView({modalIndex, open}: Props) {
171199
rendition={rendition}
172200
renditions={renditions}
173201
displayActions={!integrationOverlay}
202+
isStory={isStory}
174203
/>
175204
) : (
176205
<div></div>
@@ -183,74 +212,104 @@ export default function AssetView({modalIndex, open}: Props) {
183212
<>
184213
<Box
185214
sx={{
186-
height: dimensions.height,
187215
display: 'flex',
188216
flexDirection: 'row',
189217
justifyContent: 'space-between',
190218
}}
191219
>
192-
<Box
193-
className={
194-
filePlayerRelativeWrapperClassName
195-
}
196-
sx={theme => ({
197-
position: 'relative',
220+
<div
221+
style={{
222+
height: panelHeight,
198223
display: 'flex',
199224
flexDirection: 'column',
200-
alignItems: 'center',
201-
justifyContent: 'center',
202-
overflowY: 'auto',
203-
height: dimensions.height,
204-
width:
205-
dimensions.width + scrollbarWidth,
206-
maxWidth:
207-
dimensions.width + scrollbarWidth,
208-
backgroundColor:
209-
getMediaBackgroundColor(theme),
210-
})}
225+
}}
211226
>
212-
{rendition?.file &&
213-
(!integrationOverlay ||
214-
!integrationOverlay.replace) && (
215-
<MemoizedFilePlayer
216-
assetAnnotationsRef={
217-
assetAnnotationsRef
218-
}
219-
onNewAnnotation={
220-
onNewAnnotation
221-
}
222-
onUpdateAnnotation={
223-
onUpdateAnnotation
224-
}
225-
onDeleteAnnotation={
226-
onDeleteAnnotation
227+
<Box
228+
className={
229+
filePlayerRelativeWrapperClassName
230+
}
231+
sx={theme => ({
232+
position: 'relative',
233+
display: 'flex',
234+
flexDirection: 'column',
235+
alignItems: 'center',
236+
justifyContent: 'center',
237+
overflowY: 'auto',
238+
height: dimensions.height,
239+
width:
240+
dimensions.width +
241+
scrollbarWidth,
242+
maxWidth:
243+
dimensions.width +
244+
scrollbarWidth,
245+
backgroundColor:
246+
getMediaBackgroundColor(theme),
247+
})}
248+
>
249+
{(currentStoryAsset ||
250+
rendition?.file) &&
251+
(!integrationOverlay ||
252+
!integrationOverlay.replace) && (
253+
<MemoizedFilePlayer
254+
assetAnnotationsRef={
255+
assetAnnotationsRef
256+
}
257+
onNewAnnotation={
258+
onNewAnnotation
259+
}
260+
onUpdateAnnotation={
261+
onUpdateAnnotation
262+
}
263+
onDeleteAnnotation={
264+
onDeleteAnnotation
265+
}
266+
annotations={annotations}
267+
file={
268+
currentStoryAsset
269+
?.original?.file ||
270+
currentStoryAsset
271+
?.preview?.file ||
272+
currentStoryAsset
273+
?.thumbnail?.file ||
274+
rendition?.file
275+
}
276+
title={displayedAsset.title}
277+
dimensions={dimensions}
278+
autoPlayable={false}
279+
controls={true}
280+
zoomEnabled={true}
281+
/>
282+
)}
283+
{integrationOverlay &&
284+
React.createElement(
285+
integrationOverlay.component,
286+
{
287+
dimensions,
288+
...(integrationOverlay.props ||
289+
{}),
227290
}
228-
annotations={annotations}
229-
file={rendition.file}
230-
title={asset.title}
231-
dimensions={dimensions}
232-
autoPlayable={false}
233-
controls={true}
234-
zoomEnabled={true}
235-
/>
236-
)}
237-
{integrationOverlay &&
238-
React.createElement(
239-
integrationOverlay.component,
240-
{
241-
dimensions,
242-
...(integrationOverlay.props ||
243-
{}),
244-
}
245-
)}
246-
</Box>
291+
)}
292+
</Box>
293+
294+
{isStory ? (
295+
<StoryCarousel
296+
assets={storyAssets}
297+
selectedAsset={displayedAsset}
298+
story={asset}
299+
onAssetClick={a => {
300+
setCurrentStoryAsset(a);
301+
}}
302+
/>
303+
) : null}
304+
</div>
305+
247306
<Box
248307
sx={theme => ({
249308
width: menuWidth,
250309
maxWidth: menuWidth,
251310
borderLeft: `1px solid ${theme.palette.divider}`,
252311
overflowY: 'auto',
253-
height: dimensions.height,
312+
height: panelHeight,
254313
})}
255314
>
256315
<AssetAttributes

0 commit comments

Comments
 (0)