156 lines
4.4 KiB
TypeScript
156 lines
4.4 KiB
TypeScript
import React, { memo, useEffect, useRef } from '../../../lib/teact/teact';
|
|
import { getActions } from '../../../global';
|
|
|
|
import type { ApiMediaArea, ApiStory } from '../../../api/types';
|
|
|
|
import { MOBILE_SCREEN_MAX_WIDTH } from '../../../config';
|
|
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
|
import buildClassName from '../../../util/buildClassName';
|
|
import buildStyle from '../../../util/buildStyle';
|
|
|
|
import useWindowSize from '../../../hooks/window/useWindowSize';
|
|
|
|
import MediaAreaSuggestedReaction from './MediaAreaSuggestedReaction';
|
|
import MediaAreaWeather from './MediaAreaWeather';
|
|
|
|
import styles from './MediaArea.module.scss';
|
|
|
|
type OwnProps = {
|
|
story: ApiStory;
|
|
isActive?: boolean;
|
|
className?: string;
|
|
};
|
|
|
|
const STORY_ASPECT_RATIO = 9 / 16;
|
|
|
|
const MediaAreaOverlay = ({
|
|
story, isActive, className,
|
|
}: OwnProps) => {
|
|
const {
|
|
openMapModal, focusMessage, closeStoryViewer, openUrl,
|
|
} = getActions();
|
|
|
|
// eslint-disable-next-line no-null/no-null
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
const windowSize = useWindowSize();
|
|
|
|
useEffect(() => {
|
|
if (!ref.current || !isActive) return;
|
|
const element = ref.current;
|
|
|
|
if (windowSize.width > MOBILE_SCREEN_MAX_WIDTH) {
|
|
requestMutation(() => {
|
|
element.style.removeProperty('--media-width');
|
|
element.style.removeProperty('--media-height');
|
|
});
|
|
return;
|
|
}
|
|
|
|
const screenAspectRatio = windowSize.width / windowSize.height;
|
|
|
|
const width = screenAspectRatio < STORY_ASPECT_RATIO
|
|
? element.clientHeight * STORY_ASPECT_RATIO : element.clientWidth;
|
|
const height = screenAspectRatio < STORY_ASPECT_RATIO
|
|
? element.clientHeight : element.clientWidth / STORY_ASPECT_RATIO;
|
|
|
|
requestMutation(() => {
|
|
element.style.setProperty('--media-width', `${width}px`);
|
|
element.style.setProperty('--media-height', `${height}px`);
|
|
});
|
|
}, [isActive, windowSize]);
|
|
|
|
const handleMediaAreaClick = (mediaArea: ApiMediaArea) => {
|
|
switch (mediaArea.type) {
|
|
case 'geoPoint':
|
|
case 'venue': {
|
|
openMapModal({ geoPoint: mediaArea.geo });
|
|
break;
|
|
}
|
|
case 'channelPost': {
|
|
focusMessage({
|
|
chatId: mediaArea.channelId,
|
|
messageId: mediaArea.messageId,
|
|
});
|
|
closeStoryViewer();
|
|
break;
|
|
}
|
|
case 'url': {
|
|
openUrl({ url: mediaArea.url });
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
const mediaAreas = story.mediaAreas;
|
|
|
|
return (
|
|
<div
|
|
className={buildClassName(styles.overlay, !isActive && styles.preview, className)}
|
|
ref={ref}
|
|
>
|
|
{mediaAreas?.map((mediaArea, i) => {
|
|
switch (mediaArea.type) {
|
|
case 'geoPoint':
|
|
case 'venue':
|
|
case 'channelPost':
|
|
case 'url': {
|
|
const isShiny = isActive && (mediaArea.type !== 'channelPost');
|
|
return (
|
|
<div
|
|
className={buildClassName(styles.mediaArea, isShiny && styles.shiny)}
|
|
style={prepareStyle(mediaArea)}
|
|
onClick={() => handleMediaAreaClick(mediaArea)}
|
|
/>
|
|
);
|
|
}
|
|
case 'suggestedReaction':
|
|
return (
|
|
<MediaAreaSuggestedReaction
|
|
// eslint-disable-next-line react/no-array-index-key
|
|
key={`${mediaArea.type}-${i}`}
|
|
story={story}
|
|
mediaArea={mediaArea}
|
|
index={i}
|
|
isPreview={!isActive}
|
|
className={styles.mediaArea}
|
|
style={prepareStyle(mediaArea)}
|
|
/>
|
|
);
|
|
case 'weather': {
|
|
return (
|
|
<MediaAreaWeather
|
|
// eslint-disable-next-line react/no-array-index-key
|
|
key={`${mediaArea.type}-${i}`}
|
|
mediaArea={mediaArea}
|
|
className={styles.mediaArea}
|
|
style={prepareStyle(mediaArea)}
|
|
isPreview={isActive}
|
|
/>
|
|
);
|
|
}
|
|
default:
|
|
return undefined;
|
|
}
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function prepareStyle(mediaArea: ApiMediaArea) {
|
|
const {
|
|
x, y, width, height, rotation, radius,
|
|
} = mediaArea.coordinates;
|
|
|
|
return buildStyle(
|
|
`left: ${x}%`,
|
|
`top: ${y}%`,
|
|
`width: ${width}%`,
|
|
`height: ${height}%`,
|
|
`transform: rotate(${rotation}deg) translate(-50%, -50%)`,
|
|
Boolean(radius) && `border-radius: ${radius}%`,
|
|
);
|
|
}
|
|
|
|
export default memo(MediaAreaOverlay);
|