Paid Media: Follow-up (#4805)
This commit is contained in:
parent
9ad622e416
commit
3bd0f7130c
@ -180,7 +180,7 @@ export function buildVideoFromDocument(document: GramJs.Document, isSpoiler?: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id, mimeType, thumbs, size, attributes,
|
id, mimeType, thumbs, size, videoThumbs, attributes,
|
||||||
} = document;
|
} = document;
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
// eslint-disable-next-line no-restricted-globals
|
||||||
@ -189,14 +189,16 @@ export function buildVideoFromDocument(document: GramJs.Document, isSpoiler?: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const videoAttr = attributes
|
const videoAttr = attributes
|
||||||
.find((a: any): a is GramJs.DocumentAttributeVideo => a instanceof GramJs.DocumentAttributeVideo);
|
.find((a): a is GramJs.DocumentAttributeVideo => a instanceof GramJs.DocumentAttributeVideo);
|
||||||
|
|
||||||
if (!videoAttr) {
|
if (!videoAttr) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gifAttr = attributes
|
const gifAttr = attributes
|
||||||
.find((a: any): a is GramJs.DocumentAttributeAnimated => a instanceof GramJs.DocumentAttributeAnimated);
|
.find((a): a is GramJs.DocumentAttributeAnimated => a instanceof GramJs.DocumentAttributeAnimated);
|
||||||
|
|
||||||
|
const hasVideoPreview = videoThumbs?.some((thumb) => thumb instanceof GramJs.VideoSize && thumb.type === 'v');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
duration,
|
duration,
|
||||||
@ -221,6 +223,7 @@ export function buildVideoFromDocument(document: GramJs.Document, isSpoiler?: bo
|
|||||||
thumbnail: buildApiThumbnailFromStripped(thumbs),
|
thumbnail: buildApiThumbnailFromStripped(thumbs),
|
||||||
size: size.toJSNumber(),
|
size: size.toJSNumber(),
|
||||||
isSpoiler,
|
isSpoiler,
|
||||||
|
hasVideoPreview,
|
||||||
...(nosound && { noSound: true }),
|
...(nosound && { noSound: true }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,7 @@ export interface ApiVideo {
|
|||||||
supportsStreaming?: boolean;
|
supportsStreaming?: boolean;
|
||||||
isRound?: boolean;
|
isRound?: boolean;
|
||||||
isGif?: boolean;
|
isGif?: boolean;
|
||||||
|
hasVideoPreview?: boolean;
|
||||||
isSpoiler?: boolean;
|
isSpoiler?: boolean;
|
||||||
thumbnail?: ApiThumbnail;
|
thumbnail?: ApiThumbnail;
|
||||||
blobUrl?: string;
|
blobUrl?: string;
|
||||||
|
|||||||
@ -5,9 +5,8 @@ import React, {
|
|||||||
|
|
||||||
import type { ApiVideo } from '../../api/types';
|
import type { ApiVideo } from '../../api/types';
|
||||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||||
import { ApiMediaFormat } from '../../api/types';
|
|
||||||
|
|
||||||
import { getVideoMediaHash } from '../../global/helpers';
|
import { getVideoMediaHash, getVideoPreviewMediaHash } from '../../global/helpers';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||||
import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur';
|
import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur';
|
||||||
@ -55,10 +54,15 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||||
const loadAndPlay = isIntersecting && !isDisabled;
|
const loadAndPlay = isIntersecting && !isDisabled;
|
||||||
const previewBlobUrl = useMedia(getVideoMediaHash(gif, 'preview'), !loadAndPlay, ApiMediaFormat.BlobUrl);
|
const previewHash = !gif.hasVideoPreview && gif.thumbnail && getVideoMediaHash(gif, 'pictogram');
|
||||||
|
const previewBlobUrl = useMedia(previewHash, !loadAndPlay);
|
||||||
|
|
||||||
const [withThumb] = useState(gif.thumbnail?.dataUri && !previewBlobUrl);
|
const [withThumb] = useState(gif.thumbnail?.dataUri && !previewBlobUrl);
|
||||||
const thumbRef = useCanvasBlur(gif.thumbnail?.dataUri, !withThumb);
|
const thumbRef = useCanvasBlur(gif.thumbnail?.dataUri, !withThumb);
|
||||||
const videoData = useMedia(getVideoMediaHash(gif, 'full'), !loadAndPlay, ApiMediaFormat.BlobUrl);
|
|
||||||
|
const videoHash = getVideoPreviewMediaHash(gif) || getVideoMediaHash(gif, 'full');
|
||||||
|
const videoData = useMedia(videoHash, !loadAndPlay);
|
||||||
|
|
||||||
const shouldRenderVideo = Boolean(loadAndPlay && videoData);
|
const shouldRenderVideo = Boolean(loadAndPlay && videoData);
|
||||||
const { isBuffered, bufferingHandlers } = useBuffering(true);
|
const { isBuffered, bufferingHandlers } = useBuffering(true);
|
||||||
const shouldRenderSpinner = loadAndPlay && !isBuffered;
|
const shouldRenderSpinner = loadAndPlay && !isBuffered;
|
||||||
@ -154,8 +158,6 @@ const GifButton: FC<OwnProps> = ({
|
|||||||
<canvas
|
<canvas
|
||||||
ref={thumbRef}
|
ref={thumbRef}
|
||||||
className="thumbnail canvas-blur-setup"
|
className="thumbnail canvas-blur-setup"
|
||||||
// We need to always render to avoid blur re-calculation
|
|
||||||
style={isVideoReady ? 'display: none;' : undefined}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{previewBlobUrl && !isVideoReady && (
|
{previewBlobUrl && !isVideoReady && (
|
||||||
|
|||||||
@ -124,6 +124,7 @@ const Album: FC<OwnProps & StateProps> = ({
|
|||||||
onCancelUpload={handleCancelUpload}
|
onCancelUpload={handleCancelUpload}
|
||||||
isDownloading={photo.mediaType !== 'extendedMediaPreview' && getIsDownloading(activeDownloads, photo)}
|
isDownloading={photo.mediaType !== 'extendedMediaPreview' && getIsDownloading(activeDownloads, photo)}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
noSelectControls={album.isPaidMedia}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (video) {
|
} else if (video) {
|
||||||
@ -142,6 +143,7 @@ const Album: FC<OwnProps & StateProps> = ({
|
|||||||
onCancelUpload={handleCancelUpload}
|
onCancelUpload={handleCancelUpload}
|
||||||
isDownloading={video.mediaType !== 'extendedMediaPreview' && getIsDownloading(activeDownloads, video)}
|
isDownloading={video.mediaType !== 'extendedMediaPreview' && getIsDownloading(activeDownloads, video)}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
noSelectControls={album.isPaidMedia}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -919,7 +919,7 @@
|
|||||||
--border-bottom-left-radius: var(--border-radius-messages-small);
|
--border-bottom-left-radius: var(--border-radius-messages-small);
|
||||||
--border-bottom-right-radius: var(--border-radius-messages-small);
|
--border-bottom-right-radius: var(--border-radius-messages-small);
|
||||||
|
|
||||||
> .media-inner {
|
.media-inner {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
margin-bottom: 0.25rem !important;
|
margin-bottom: 0.25rem !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import useLastCallback from '../../../../hooks/useLastCallback';
|
|||||||
type OwnProps<T> =
|
type OwnProps<T> =
|
||||||
(PhotoProps<T> | VideoProps<T>) & {
|
(PhotoProps<T> | VideoProps<T>) & {
|
||||||
clickArg: number;
|
clickArg: number;
|
||||||
|
noSelectControls?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -76,10 +77,10 @@ export default function withSelectControl(WrappedComponent: FC) {
|
|||||||
|
|
||||||
return memo(withGlobal<OwnProps<unknown>>(
|
return memo(withGlobal<OwnProps<unknown>>(
|
||||||
(global, ownProps) => {
|
(global, ownProps) => {
|
||||||
const { clickArg } = ownProps;
|
const { clickArg, noSelectControls } = ownProps;
|
||||||
return {
|
return {
|
||||||
isInSelectMode: selectIsInSelectMode(global),
|
isInSelectMode: !noSelectControls && selectIsInSelectMode(global),
|
||||||
isSelected: selectIsMessageSelected(global, clickArg),
|
isSelected: !noSelectControls && selectIsMessageSelected(global, clickArg),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(ComponentWithSelectControl)) as typeof ComponentWithSelectControl;
|
)(ComponentWithSelectControl)) as typeof ComponentWithSelectControl;
|
||||||
|
|||||||
@ -104,7 +104,6 @@ export default function useInnerHandlers(
|
|||||||
});
|
});
|
||||||
const openMediaViewerWithPhotoOrVideo = useLastCallback((withDynamicLoading: boolean): void => {
|
const openMediaViewerWithPhotoOrVideo = useLastCallback((withDynamicLoading: boolean): void => {
|
||||||
if (paidMedia && !paidMedia.isBought) return;
|
if (paidMedia && !paidMedia.isBought) return;
|
||||||
if (paidMedia) return; // TODO: Implement MV and remove this line
|
|
||||||
if (withDynamicLoading) {
|
if (withDynamicLoading) {
|
||||||
searchChatMediaMessages({ chatId, threadId, currentMediaMessageId: messageId });
|
searchChatMediaMessages({ chatId, threadId, currentMediaMessageId: messageId });
|
||||||
}
|
}
|
||||||
@ -117,12 +116,12 @@ export default function useInnerHandlers(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
const handlePhotoMediaClick = useLastCallback((): void => {
|
const handlePhotoMediaClick = useLastCallback((): void => {
|
||||||
const withDynamicLoading = !isScheduled;
|
const withDynamicLoading = !isScheduled && !paidMedia;
|
||||||
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
||||||
});
|
});
|
||||||
const handleVideoMediaClick = useLastCallback(() => {
|
const handleVideoMediaClick = useLastCallback(() => {
|
||||||
const isGif = message.content?.video?.isGif;
|
const isGif = message.content?.video?.isGif;
|
||||||
const withDynamicLoading = !isGif && !isScheduled;
|
const withDynamicLoading = !isGif && !isScheduled && !paidMedia;
|
||||||
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
openMediaViewerWithPhotoOrVideo(withDynamicLoading);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { getUserFullName } from '../../../global/helpers';
|
|||||||
import { selectUser } from '../../../global/selectors';
|
import { selectUser } from '../../../global/selectors';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
import { formatCurrency } from '../../../util/formatCurrency';
|
||||||
|
import { formatInteger } from '../../../util/textFormat';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -233,7 +234,7 @@ function StarTopupOption({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.option} key={option.stars} onClick={() => onClick?.(option)}>
|
<div className={styles.option} key={option.stars} onClick={() => onClick?.(option)}>
|
||||||
<div className={styles.optionTop}>
|
<div className={styles.optionTop}>
|
||||||
+{option.stars}
|
+{formatInteger(option.stars)}
|
||||||
{/* Switch directionality for correct order. Can't use flex because https://issues.chromium.org/issues/40249030 */}
|
{/* Switch directionality for correct order. Can't use flex because https://issues.chromium.org/issues/40249030 */}
|
||||||
<div className={styles.stackedStars} dir={lang.isRtl ? 'ltr' : 'rtl'}>
|
<div className={styles.stackedStars} dir={lang.isRtl ? 'ltr' : 'rtl'}>
|
||||||
{Array.from({ length: starsCount }).map(() => (
|
{Array.from({ length: starsCount }).map(() => (
|
||||||
|
|||||||
@ -699,7 +699,9 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
|||||||
} else {
|
} else {
|
||||||
const content = media as MediaContent;
|
const content = media as MediaContent;
|
||||||
global = updateChatMessage(global, chatId, id, {
|
global = updateChatMessage(global, chatId, id, {
|
||||||
content,
|
content: {
|
||||||
|
...content,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -319,6 +319,10 @@ export function getVideoMediaHash(video: ApiVideo | ApiDocument, target: Target)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getVideoPreviewMediaHash(video: ApiVideo) {
|
||||||
|
return video.hasVideoPreview ? `document${video.id}?size=v` : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDocumentMediaHash(document: ApiDocument, target: Target) {
|
export function getDocumentMediaHash(document: ApiDocument, target: Target) {
|
||||||
const base = `document${document.id}`;
|
const base = `document${document.id}`;
|
||||||
|
|
||||||
|
|||||||
@ -1245,7 +1245,7 @@ export function selectCanForwardMessages<T extends GlobalState>(global: T, chatI
|
|||||||
|
|
||||||
return messageIds
|
return messageIds
|
||||||
.map((id) => messages[id])
|
.map((id) => messages[id])
|
||||||
.every((message) => !hasMessageTtl(message)
|
.every((message) => message && !hasMessageTtl(message)
|
||||||
&& (message.isForwardingAllowed || isServiceNotificationMessage(message)));
|
&& (message.isForwardingAllowed || isServiceNotificationMessage(message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user