Invoice Media: Better animation; Message Select Toolbar: Hide unavailable actions (#2121)
This commit is contained in:
parent
d10df1a705
commit
ecd4155fff
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
BIN
src/assets/turbulence_1x.png
Normal file
BIN
src/assets/turbulence_1x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 17 KiB |
BIN
src/assets/turbulence_3x.png
Normal file
BIN
src/assets/turbulence_3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@ -7,20 +7,21 @@ import type { MessageListType } from '../../global/types';
|
|||||||
import {
|
import {
|
||||||
selectCanDeleteSelectedMessages,
|
selectCanDeleteSelectedMessages,
|
||||||
selectCanDownloadSelectedMessages,
|
selectCanDownloadSelectedMessages,
|
||||||
|
selectCanForwardMessages,
|
||||||
selectCanReportSelectedMessages,
|
selectCanReportSelectedMessages,
|
||||||
selectCurrentMessageList,
|
selectCurrentMessageList,
|
||||||
selectHasProtectedMessage,
|
selectHasProtectedMessage,
|
||||||
selectSelectedMessagesCount,
|
selectSelectedMessagesCount,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import useFlag from '../../hooks/useFlag';
|
|
||||||
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
|
import useFlag from '../../hooks/useFlag';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useCopySelectedMessages from './hooks/useCopySelectedMessages';
|
import useCopySelectedMessages from './hooks/useCopySelectedMessages';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
|
|
||||||
import DeleteSelectedMessageModal from './DeleteSelectedMessageModal';
|
import DeleteSelectedMessageModal from './DeleteSelectedMessageModal';
|
||||||
import ReportModal from '../common/ReportModal';
|
import ReportModal from '../common/ReportModal';
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ type StateProps = {
|
|||||||
canDeleteMessages?: boolean;
|
canDeleteMessages?: boolean;
|
||||||
canReportMessages?: boolean;
|
canReportMessages?: boolean;
|
||||||
canDownloadMessages?: boolean;
|
canDownloadMessages?: boolean;
|
||||||
|
canForwardMessages?: boolean;
|
||||||
hasProtectedMessage?: boolean;
|
hasProtectedMessage?: boolean;
|
||||||
isAnyModalOpen?: boolean;
|
isAnyModalOpen?: boolean;
|
||||||
selectedMessageIds?: number[];
|
selectedMessageIds?: number[];
|
||||||
@ -52,6 +54,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
canDeleteMessages,
|
canDeleteMessages,
|
||||||
canReportMessages,
|
canReportMessages,
|
||||||
canDownloadMessages,
|
canDownloadMessages,
|
||||||
|
canForwardMessages,
|
||||||
hasProtectedMessage,
|
hasProtectedMessage,
|
||||||
isAnyModalOpen,
|
isAnyModalOpen,
|
||||||
selectedMessageIds,
|
selectedMessageIds,
|
||||||
@ -107,7 +110,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const renderButton = (
|
const renderButton = (
|
||||||
icon: string, label: string, onClick: AnyToVoidFunction, disabled?: boolean, destructive?: boolean,
|
icon: string, label: string, onClick: AnyToVoidFunction, destructive?: boolean,
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -115,10 +118,9 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
'item',
|
'item',
|
||||||
disabled && 'disabled',
|
|
||||||
destructive && 'destructive',
|
destructive && 'destructive',
|
||||||
)}
|
)}
|
||||||
onClick={!disabled ? onClick : undefined}
|
onClick={onClick}
|
||||||
title={label}
|
title={label}
|
||||||
>
|
>
|
||||||
<i className={`icon-${icon}`} />
|
<i className={`icon-${icon}`} />
|
||||||
@ -143,19 +145,23 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
{Boolean(selectedMessagesCount) && (
|
{Boolean(selectedMessagesCount) && (
|
||||||
<div className="MessageSelectToolbar-actions">
|
<div className="MessageSelectToolbar-actions">
|
||||||
{messageListType !== 'scheduled' && (
|
{messageListType !== 'scheduled' && canForwardMessages && (
|
||||||
renderButton(
|
renderButton(
|
||||||
'forward', lang('Chat.ForwardActionHeader'), openForwardMenuForSelectedMessages, hasProtectedMessage,
|
'forward', lang('Chat.ForwardActionHeader'), openForwardMenuForSelectedMessages,
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{canReportMessages && (
|
{canReportMessages && (
|
||||||
renderButton('flag', lang('Conversation.ReportMessages'), openReportModal)
|
renderButton('flag', lang('Conversation.ReportMessages'), openReportModal)
|
||||||
)}
|
)}
|
||||||
{canDownloadMessages && (
|
{canDownloadMessages && !hasProtectedMessage && (
|
||||||
renderButton('download', lang('lng_media_download'), handleDownload, hasProtectedMessage)
|
renderButton('download', lang('lng_media_download'), handleDownload)
|
||||||
|
)}
|
||||||
|
{!hasProtectedMessage && (
|
||||||
|
renderButton('copy', lang('lng_context_copy_selected_items'), handleCopy)
|
||||||
|
)}
|
||||||
|
{canDeleteMessages && (
|
||||||
|
renderButton('delete', lang('EditAdminGroupDeleteMessages'), openDeleteModal, true)
|
||||||
)}
|
)}
|
||||||
{renderButton('copy', lang('lng_context_copy_selected_items'), handleCopy, hasProtectedMessage)}
|
|
||||||
{renderButton('delete', lang('EditAdminGroupDeleteMessages'), openDeleteModal, !canDeleteMessages, true)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -182,6 +188,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const canDownload = selectCanDownloadSelectedMessages(global);
|
const canDownload = selectCanDownloadSelectedMessages(global);
|
||||||
const { messageIds: selectedMessageIds } = global.selectedMessages || {};
|
const { messageIds: selectedMessageIds } = global.selectedMessages || {};
|
||||||
const hasProtectedMessage = chatId ? selectHasProtectedMessage(global, chatId, selectedMessageIds) : false;
|
const hasProtectedMessage = chatId ? selectHasProtectedMessage(global, chatId, selectedMessageIds) : false;
|
||||||
|
const canForward = chatId ? selectCanForwardMessages(global, chatId, selectedMessageIds) : false;
|
||||||
const isForwardModalOpen = global.forwardMessages.isModalShown;
|
const isForwardModalOpen = global.forwardMessages.isModalShown;
|
||||||
const isAnyModalOpen = Boolean(isForwardModalOpen || global.requestedDraft
|
const isAnyModalOpen = Boolean(isForwardModalOpen || global.requestedDraft
|
||||||
|| global.requestedAttachBotInChat || global.requestedAttachBotInstall);
|
|| global.requestedAttachBotInChat || global.requestedAttachBotInstall);
|
||||||
@ -192,6 +199,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
canDeleteMessages: canDelete,
|
canDeleteMessages: canDelete,
|
||||||
canReportMessages: canReport,
|
canReportMessages: canReport,
|
||||||
canDownloadMessages: canDownload,
|
canDownloadMessages: canDownload,
|
||||||
|
canForwardMessages: canForward,
|
||||||
selectedMessageIds,
|
selectedMessageIds,
|
||||||
hasProtectedMessage,
|
hasProtectedMessage,
|
||||||
isAnyModalOpen,
|
isAnyModalOpen,
|
||||||
|
|||||||
@ -13,20 +13,23 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
--background-url: url('../../../assets/turbulence.png');
|
--background-url: url('../../../assets/turbulence_1x.png');
|
||||||
--background-size: 256px;
|
--background-size: 256px;
|
||||||
background: rgba(0, 0, 0, 0.3) var(--background-url);
|
background: rgba(0, 0, 0, 0.25) var(--background-url);
|
||||||
background-size: var(--background-size) var(--background-size);
|
background-size: var(--background-size) var(--background-size);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
@media (-webkit-min-device-pixel-ratio: 2) {
|
||||||
|
--background-url: url('../../../assets/turbulence_2x.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (-webkit-min-device-pixel-ratio: 3) {
|
||||||
|
--background-url: url('../../../assets/turbulence_3x.png');
|
||||||
|
}
|
||||||
|
|
||||||
--x-direction: var(--background-size);
|
--x-direction: var(--background-size);
|
||||||
--y-direction: 0;
|
--y-direction: 0;
|
||||||
animation: 10s linear infinite dots;
|
animation: 20s linear infinite dots;
|
||||||
|
|
||||||
&.highres {
|
|
||||||
--background-url: url('../../../assets/turbulence_2x.png');
|
|
||||||
--background-size: 128px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
@ -40,7 +43,7 @@
|
|||||||
|
|
||||||
--x-direction: 0;
|
--x-direction: 0;
|
||||||
--y-direction: var(--background-size);
|
--y-direction: var(--background-size);
|
||||||
animation: 10s linear -4s infinite dots;
|
animation: 20s linear -7s infinite dots;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
@ -55,7 +58,7 @@
|
|||||||
|
|
||||||
--x-direction: calc(-1 * var(--background-size));
|
--x-direction: calc(-1 * var(--background-size));
|
||||||
--y-direction: calc(-1 * var(--background-size));
|
--y-direction: calc(-1 * var(--background-size));
|
||||||
animation: 10s linear -8s infinite dots;
|
animation: 20s linear -14s infinite dots;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,16 +88,13 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
padding: 0.375rem 0.625rem;
|
padding: 0.5rem 0.875rem;
|
||||||
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
line-height: 1rem;
|
||||||
|
|
||||||
.lock {
|
|
||||||
margin-bottom: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas {
|
.canvas {
|
||||||
@ -105,13 +105,8 @@
|
|||||||
@keyframes dots {
|
@keyframes dots {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: var(--x-direction) var(--y-direction);
|
background-position: var(--x-direction) var(--y-direction);
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { getMessageInvoice } from '../../../global/helpers';
|
|||||||
import { formatCurrency } from '../../../util/formatCurrency';
|
import { formatCurrency } from '../../../util/formatCurrency';
|
||||||
import { formatMediaDuration } from '../../../util/dateFormat';
|
import { formatMediaDuration } from '../../../util/dateFormat';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import { DPR } from '../../../util/environment';
|
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useCanvasBlur from '../../../hooks/useCanvasBlur';
|
import useCanvasBlur from '../../../hooks/useCanvasBlur';
|
||||||
@ -66,7 +65,7 @@ const InvoiceMediaPreview: FC<OwnProps> = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<canvas ref={canvasRef} className={styles.canvas} width={width} height={height} />
|
<canvas ref={canvasRef} className={styles.canvas} width={width} height={height} />
|
||||||
<div className={buildClassName(styles.dots, DPR > 1 && styles.highres)} />
|
<div className={styles.dots} />
|
||||||
{Boolean(duration) && <div className={styles.duration}>{formatMediaDuration(duration)}</div>}
|
{Boolean(duration) && <div className={styles.duration}>{formatMediaDuration(duration)}</div>}
|
||||||
<div className={styles.buy}>
|
<div className={styles.buy}>
|
||||||
<i className={buildClassName('icon-lock', styles.lock)} />
|
<i className={buildClassName('icon-lock', styles.lock)} />
|
||||||
|
|||||||
@ -399,7 +399,7 @@ export function selectAllowedMessageActions(global: GlobalState, message: ApiMes
|
|||||||
|| getServerTime(global.serverTimeOffset) - message.date < MESSAGE_EDIT_ALLOWED_TIME
|
|| getServerTime(global.serverTimeOffset) - message.date < MESSAGE_EDIT_ALLOWED_TIME
|
||||||
) && !(
|
) && !(
|
||||||
content.sticker || content.contact || content.poll || content.action || content.audio
|
content.sticker || content.contact || content.poll || content.action || content.audio
|
||||||
|| (content.video?.isRound) || content.location
|
|| (content.video?.isRound) || content.location || content.invoice
|
||||||
)
|
)
|
||||||
&& !isForwardedMessage(message)
|
&& !isForwardedMessage(message)
|
||||||
&& !message.viaBotId
|
&& !message.viaBotId
|
||||||
@ -922,6 +922,20 @@ export function selectHasProtectedMessage(global: GlobalState, chatId: string, m
|
|||||||
return messageIds.some((messageId) => messages[messageId]?.isProtected);
|
return messageIds.some((messageId) => messages[messageId]?.isProtected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectCanForwardMessages(global: GlobalState, chatId: string, messageIds?: number[]) {
|
||||||
|
if (selectChat(global, chatId)?.isProtected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!messageIds) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = selectChatMessages(global, chatId);
|
||||||
|
|
||||||
|
return messageIds.every((messageId) => messages[messageId]?.isForwardingAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
export function selectSponsoredMessage(global: GlobalState, chatId: string) {
|
export function selectSponsoredMessage(global: GlobalState, chatId: string) {
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
const message = chat && isChatChannel(chat) ? global.messages.sponsoredByChatId[chatId] : undefined;
|
const message = chat && isChatChannel(chat) ? global.messages.sponsoredByChatId[chatId] : undefined;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user