mirror of
https://github.com/danog/telegram-tt.git
synced 2024-11-26 20:34:44 +01:00
Sticker Picker: Support clearing recent, allow viewing set from context menu (#1852)
This commit is contained in:
parent
efa4c59ae0
commit
ccaa82bec1
@ -40,6 +40,7 @@ export {
|
||||
fetchStickerSets, fetchRecentStickers, fetchFavoriteStickers, fetchFeaturedStickers,
|
||||
faveSticker, fetchStickers, fetchSavedGifs, saveGif, searchStickers, installStickerSet, uninstallStickerSet,
|
||||
searchGifs, fetchAnimatedEmojis, fetchStickersForEmoji, fetchEmojiKeywords, fetchAnimatedEmojiEffects,
|
||||
removeRecentSticker, clearRecentStickers,
|
||||
} from './symbols';
|
||||
|
||||
export {
|
||||
|
@ -94,6 +94,23 @@ export async function faveSticker({
|
||||
}
|
||||
}
|
||||
|
||||
export function removeRecentSticker({
|
||||
sticker,
|
||||
}: {
|
||||
sticker: ApiSticker;
|
||||
}) {
|
||||
const request = new GramJs.messages.SaveRecentSticker({
|
||||
id: buildInputDocument(sticker),
|
||||
unsave: true,
|
||||
});
|
||||
|
||||
return invokeRequest(request);
|
||||
}
|
||||
|
||||
export function clearRecentStickers() {
|
||||
return invokeRequest(new GramJs.messages.ClearRecentStickers());
|
||||
}
|
||||
|
||||
export async function fetchStickers(
|
||||
{ stickerSetShortName, stickerSetId, accessHash }:
|
||||
{ stickerSetShortName?: string; stickerSetId?: string; accessHash: string },
|
||||
|
@ -49,7 +49,7 @@ import {
|
||||
getGroupCallId,
|
||||
} from './apiBuilders/calls';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers';
|
||||
import { buildApiEmojiInteraction } from './apiBuilders/symbols';
|
||||
import { buildApiEmojiInteraction, buildStickerSet } from './apiBuilders/symbols';
|
||||
import { buildApiBotMenuButton } from './apiBuilders/bots';
|
||||
|
||||
type Update = (
|
||||
@ -381,8 +381,7 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
|
||||
|| originRequest instanceof GramJs.messages.SendMultiMedia
|
||||
|| originRequest instanceof GramJs.messages.ForwardMessages
|
||||
) && (
|
||||
update instanceof GramJs.UpdateMessageID
|
||||
|| update instanceof GramJs.UpdateShortSentMessage
|
||||
update instanceof GramJs.UpdateMessageID || update instanceof GramJs.UpdateShortSentMessage
|
||||
)) {
|
||||
let randomId;
|
||||
if ('randomId' in update) {
|
||||
@ -861,6 +860,23 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
|
||||
onUpdate({ '@type': 'updateResetContactList' });
|
||||
} else if (update instanceof GramJs.UpdateFavedStickers) {
|
||||
onUpdate({ '@type': 'updateFavoriteStickers' });
|
||||
} else if (update instanceof GramJs.UpdateRecentStickers) {
|
||||
onUpdate({ '@type': 'updateRecentStickers' });
|
||||
} else if (update instanceof GramJs.UpdateStickerSets) {
|
||||
onUpdate({ '@type': 'updateStickerSets' });
|
||||
} else if (update instanceof GramJs.UpdateStickerSetsOrder) {
|
||||
onUpdate({ '@type': 'updateStickerSetsOrder', order: update.order.map((n) => n.toString()) });
|
||||
} else if (update instanceof GramJs.UpdateNewStickerSet) {
|
||||
if (update.stickerset instanceof GramJs.messages.StickerSet) {
|
||||
const stickerSet = buildStickerSet(update.stickerset.set);
|
||||
onUpdate({
|
||||
'@type': 'updateStickerSet',
|
||||
id: stickerSet.id,
|
||||
stickerSet,
|
||||
});
|
||||
}
|
||||
} else if (update instanceof GramJs.UpdateSavedGifs) {
|
||||
onUpdate({ '@type': 'updateSavedGifs' });
|
||||
} else if (update instanceof GramJs.UpdateGroupCall) {
|
||||
onUpdate({
|
||||
'@type': 'updateGroupCall',
|
||||
|
@ -355,12 +355,29 @@ export type ApiUpdateFavoriteStickers = {
|
||||
'@type': 'updateFavoriteStickers';
|
||||
};
|
||||
|
||||
export type ApiUpdateRecentStickers = {
|
||||
'@type': 'updateRecentStickers';
|
||||
};
|
||||
|
||||
export type ApiUpdateStickerSets = {
|
||||
'@type': 'updateStickerSets';
|
||||
};
|
||||
|
||||
export type ApiUpdateStickerSetsOrder = {
|
||||
'@type': 'updateStickerSetsOrder';
|
||||
order: string[];
|
||||
};
|
||||
|
||||
export type ApiUpdateStickerSet = {
|
||||
'@type': 'updateStickerSet';
|
||||
id: string;
|
||||
stickerSet: Partial<ApiStickerSet>;
|
||||
};
|
||||
|
||||
export type ApiUpdateSavedGifs = {
|
||||
'@type': 'updateSavedGifs';
|
||||
};
|
||||
|
||||
export type ApiUpdateTwoFaError = {
|
||||
'@type': 'updateTwoFaError';
|
||||
message: string;
|
||||
@ -511,8 +528,9 @@ export type ApiUpdate = (
|
||||
ApiDeleteContact | ApiUpdateUser | ApiUpdateUserStatus | ApiUpdateUserFullInfo | ApiUpdateDeleteProfilePhotos |
|
||||
ApiUpdateAvatar | ApiUpdateMessageImage | ApiUpdateDraftMessage |
|
||||
ApiUpdateError | ApiUpdateResetContacts | ApiUpdateStartEmojiInteraction |
|
||||
ApiUpdateFavoriteStickers | ApiUpdateStickerSet |
|
||||
ApiUpdateNewScheduledMessage | ApiUpdateScheduledMessageSendSucceeded | ApiUpdateScheduledMessage |
|
||||
ApiUpdateFavoriteStickers | ApiUpdateStickerSet | ApiUpdateStickerSets | ApiUpdateStickerSetsOrder |
|
||||
ApiUpdateRecentStickers | ApiUpdateSavedGifs | ApiUpdateNewScheduledMessage |
|
||||
ApiUpdateScheduledMessageSendSucceeded | ApiUpdateScheduledMessage |
|
||||
ApiUpdateDeleteScheduledMessages | ApiUpdateResetMessages |
|
||||
ApiUpdateTwoFaError | ApiUpdateTwoFaStateWaitCode | ApiUpdateWebViewResultSent |
|
||||
ApiUpdateNotifySettings | ApiUpdateNotifyExceptions | ApiUpdatePeerBlocked | ApiUpdatePrivacy |
|
||||
|
@ -15,7 +15,7 @@
|
||||
&:hover {
|
||||
background-color: var(--color-interactive-element-hover);
|
||||
|
||||
.sticker-unfave-button {
|
||||
.sticker-remove-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.sticker-unfave-button {
|
||||
.sticker-remove-button {
|
||||
position: absolute;
|
||||
top: -0.5rem;
|
||||
right: -0.5rem;
|
||||
@ -71,6 +71,7 @@
|
||||
|
||||
.sticker-context-menu {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
|
||||
.bubble {
|
||||
width: auto;
|
||||
|
@ -2,6 +2,7 @@ import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions } from '../../global';
|
||||
|
||||
import { ApiBotInlineMediaResult, ApiMediaFormat, ApiSticker } from '../../api/types';
|
||||
|
||||
@ -34,10 +35,12 @@ type OwnProps<T> = {
|
||||
clickArg: T;
|
||||
noContextMenu?: boolean;
|
||||
isSavedMessages?: boolean;
|
||||
canViewSet?: boolean;
|
||||
observeIntersection: ObserveFn;
|
||||
onClick?: (arg: OwnProps<T>['clickArg'], isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||
onFaveClick?: (sticker: ApiSticker) => void;
|
||||
onUnfaveClick?: (sticker: ApiSticker) => void;
|
||||
onRemoveRecentClick?: (sticker: ApiSticker) => void;
|
||||
};
|
||||
|
||||
const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult | undefined = undefined>({
|
||||
@ -49,11 +52,14 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
clickArg,
|
||||
noContextMenu,
|
||||
isSavedMessages,
|
||||
canViewSet,
|
||||
observeIntersection,
|
||||
onClick,
|
||||
onFaveClick,
|
||||
onUnfaveClick,
|
||||
onRemoveRecentClick,
|
||||
}: OwnProps<T>) => {
|
||||
const { openStickerSet } = getActions();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const lang = useLang();
|
||||
@ -140,12 +146,16 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
handleBeforeContextMenu(e);
|
||||
};
|
||||
|
||||
const handleUnfaveClick = useCallback((e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
const handleRemoveClick = useCallback((e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
onUnfaveClick!(sticker);
|
||||
}, [onUnfaveClick, sticker]);
|
||||
onRemoveRecentClick!(sticker);
|
||||
}, [onRemoveRecentClick, sticker]);
|
||||
|
||||
const handleContextRemoveRecent = useCallback(() => {
|
||||
onRemoveRecentClick!(sticker);
|
||||
}, [onRemoveRecentClick, sticker]);
|
||||
|
||||
const handleContextUnfave = useCallback(() => {
|
||||
onUnfaveClick!(sticker);
|
||||
@ -163,6 +173,12 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
onClick?.(clickArg, undefined, true);
|
||||
}, [clickArg, onClick]);
|
||||
|
||||
const handleOpenSet = useCallback(() => {
|
||||
openStickerSet({ sticker });
|
||||
}, [openStickerSet, sticker]);
|
||||
|
||||
const shouldShowCloseButton = !IS_TOUCH_ENV && onRemoveRecentClick;
|
||||
|
||||
const fullClassName = buildClassName(
|
||||
'StickerButton',
|
||||
onClick && 'interactive',
|
||||
@ -207,12 +223,12 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
onLoad={markLoaded}
|
||||
/>
|
||||
)}
|
||||
{!IS_TOUCH_ENV && onUnfaveClick && (
|
||||
{shouldShowCloseButton && (
|
||||
<Button
|
||||
className="sticker-unfave-button"
|
||||
className="sticker-remove-button"
|
||||
color="dark"
|
||||
round
|
||||
onClick={handleUnfaveClick}
|
||||
onClick={handleRemoveClick}
|
||||
>
|
||||
<i className="icon-close" />
|
||||
</Button>
|
||||
@ -244,6 +260,16 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
<MenuItem onClick={handleSendScheduled} icon="calendar">
|
||||
{lang(isSavedMessages ? 'SetReminder' : 'ScheduleMessage')}
|
||||
</MenuItem>
|
||||
{canViewSet && (
|
||||
<MenuItem onClick={handleOpenSet} icon="stickers">
|
||||
{lang('ViewPackPreview')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{onRemoveRecentClick && (
|
||||
<MenuItem icon="delete" onClick={handleContextRemoveRecent}>
|
||||
{lang('DeleteFromRecent')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { FC, memo, useRef } from '../../../lib/teact/teact';
|
||||
|
||||
import { RECENT_SYMBOL_SET_ID } from '../../../config';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import windowSize from '../../../util/windowSize';
|
||||
|
||||
import { ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||
import useMediaTransition from '../../../hooks/useMediaTransition';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -47,9 +49,11 @@ const EmojiCategory: FC<OwnProps> = ({
|
||||
id={`emoji-category-${index}`}
|
||||
className="symbol-set"
|
||||
>
|
||||
<p className="symbol-set-name" dir="auto">
|
||||
{lang(category.id === 'recent' ? 'RecentStickers' : `Emoji${index}`)}
|
||||
</p>
|
||||
<div className="symbol-set-header">
|
||||
<p className="symbol-set-name" dir="auto">
|
||||
{lang(category.id === RECENT_SYMBOL_SET_ID ? 'RecentStickers' : `Emoji${index}`)}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={buildClassName('symbol-set-container', transitionClassNames)}
|
||||
style={`height: ${height}px;`}
|
||||
|
@ -5,7 +5,7 @@ import { withGlobal } from '../../../global';
|
||||
|
||||
import { GlobalState } from '../../../global/types';
|
||||
|
||||
import { MENU_TRANSITION_DURATION } from '../../../config';
|
||||
import { MENU_TRANSITION_DURATION, RECENT_SYMBOL_SET_ID } from '../../../config';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../util/environment';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import {
|
||||
@ -126,7 +126,7 @@ const EmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
const themeCategories = [...categories];
|
||||
if (recentEmojis?.length) {
|
||||
themeCategories.unshift({
|
||||
id: 'recent',
|
||||
id: RECENT_SYMBOL_SET_ID,
|
||||
name: lang('RecentStickers'),
|
||||
emojis: recentEmojis,
|
||||
});
|
||||
|
@ -6,7 +6,9 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import { ApiStickerSet, ApiSticker } from '../../../api/types';
|
||||
import { StickerSetOrRecent } from '../../../types';
|
||||
|
||||
import { SLIDE_TRANSITION_DURATION, STICKER_SIZE_PICKER_HEADER } from '../../../config';
|
||||
import {
|
||||
FAVORITE_SYMBOL_SET_ID, RECENT_SYMBOL_SET_ID, SLIDE_TRANSITION_DURATION, STICKER_SIZE_PICKER_HEADER,
|
||||
} from '../../../config';
|
||||
import { IS_TOUCH_ENV } from '../../../util/environment';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import fastSmoothScroll from '../../../util/fastSmoothScroll';
|
||||
@ -72,6 +74,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
addRecentSticker,
|
||||
unfaveSticker,
|
||||
faveSticker,
|
||||
removeRecentSticker,
|
||||
} = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
@ -116,19 +119,28 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
return MEMO_EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'recent',
|
||||
title: lang('RecentStickers'),
|
||||
stickers: recentStickers,
|
||||
count: recentStickers.length,
|
||||
},
|
||||
{
|
||||
id: 'favorite',
|
||||
const defaultSets = [];
|
||||
|
||||
if (favoriteStickers.length) {
|
||||
defaultSets.push({
|
||||
id: FAVORITE_SYMBOL_SET_ID,
|
||||
title: lang('FavoriteStickers'),
|
||||
stickers: favoriteStickers,
|
||||
count: favoriteStickers.length,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (recentStickers.length) {
|
||||
defaultSets.push({
|
||||
id: RECENT_SYMBOL_SET_ID,
|
||||
title: lang('RecentStickers'),
|
||||
stickers: recentStickers,
|
||||
count: recentStickers.length,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
...defaultSets,
|
||||
...addedSetIds.map((id) => stickerSetsById[id]).filter(Boolean),
|
||||
];
|
||||
}, [addedSetIds, lang, recentStickers, favoriteStickers, stickerSetsById]);
|
||||
@ -186,6 +198,10 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
sendMessageAction({ type: 'chooseSticker' });
|
||||
}, [sendMessageAction]);
|
||||
|
||||
const handleRemoveRecentSticker = useCallback((sticker: ApiSticker) => {
|
||||
removeRecentSticker({ sticker });
|
||||
}, [removeRecentSticker]);
|
||||
|
||||
const canRenderContents = useAsyncRendering([], SLIDE_TRANSITION_DURATION);
|
||||
|
||||
function renderCover(stickerSet: StickerSetOrRecent, index: number) {
|
||||
@ -195,21 +211,24 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
index === activeSetIndex && 'activated',
|
||||
);
|
||||
|
||||
if (stickerSet.id === 'recent' || stickerSet.id === 'favorite' || stickerSet.hasThumbnail || !firstSticker) {
|
||||
if (stickerSet.id === RECENT_SYMBOL_SET_ID
|
||||
|| stickerSet.id === FAVORITE_SYMBOL_SET_ID
|
||||
|| stickerSet.hasThumbnail
|
||||
|| !firstSticker) {
|
||||
return (
|
||||
<Button
|
||||
key={stickerSet.id}
|
||||
className={buttonClassName}
|
||||
ariaLabel={stickerSet.title}
|
||||
round
|
||||
faded={stickerSet.id === 'recent' || stickerSet.id === 'favorite'}
|
||||
faded={stickerSet.id === RECENT_SYMBOL_SET_ID || stickerSet.id === FAVORITE_SYMBOL_SET_ID}
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => selectStickerSet(index)}
|
||||
>
|
||||
{stickerSet.id === 'recent' ? (
|
||||
{stickerSet.id === RECENT_SYMBOL_SET_ID ? (
|
||||
<i className="icon-recent" />
|
||||
) : stickerSet.id === 'favorite' ? (
|
||||
) : stickerSet.id === FAVORITE_SYMBOL_SET_ID ? (
|
||||
<i className="icon-favorite" />
|
||||
) : stickerSet.isLottie ? (
|
||||
<StickerSetCoverAnimated
|
||||
@ -281,6 +300,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
onStickerSelect={handleStickerSelect}
|
||||
onStickerUnfave={handleStickerUnfave}
|
||||
onStickerFave={handleStickerFave}
|
||||
onStickerRemoveRecent={handleRemoveRecentSticker}
|
||||
favoriteStickers={favoriteStickers}
|
||||
isSavedMessages={isSavedMessages}
|
||||
/>
|
||||
|
@ -1,19 +1,23 @@
|
||||
import React, {
|
||||
FC, memo, useMemo, useRef,
|
||||
FC, memo, useCallback, useMemo, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import { ApiSticker } from '../../../api/types';
|
||||
import { StickerSetOrRecent } from '../../../types';
|
||||
import { ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import { STICKER_SIZE_PICKER } from '../../../config';
|
||||
import { FAVORITE_SYMBOL_SET_ID, RECENT_SYMBOL_SET_ID, STICKER_SIZE_PICKER } from '../../../config';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||
import windowSize from '../../../util/windowSize';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useMediaTransition from '../../../hooks/useMediaTransition';
|
||||
|
||||
import StickerButton from '../../common/StickerButton';
|
||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||
|
||||
type OwnProps = {
|
||||
stickerSet: StickerSetOrRecent;
|
||||
@ -26,6 +30,7 @@ type OwnProps = {
|
||||
onStickerSelect: (sticker: ApiSticker, isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||
onStickerUnfave: (sticker: ApiSticker) => void;
|
||||
onStickerFave: (sticker: ApiSticker) => void;
|
||||
onStickerRemoveRecent: (sticker: ApiSticker) => void;
|
||||
};
|
||||
|
||||
const STICKERS_PER_ROW_ON_DESKTOP = 5;
|
||||
@ -43,14 +48,23 @@ const StickerSet: FC<OwnProps> = ({
|
||||
onStickerSelect,
|
||||
onStickerUnfave,
|
||||
onStickerFave,
|
||||
onStickerRemoveRecent,
|
||||
}) => {
|
||||
const { clearRecentStickers } = getActions();
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [isConfirmModalOpen, openConfirmModal, closeConfirmModal] = useFlag();
|
||||
const lang = useLang();
|
||||
|
||||
useOnIntersect(ref, observeIntersection);
|
||||
|
||||
const transitionClassNames = useMediaTransition(shouldRender);
|
||||
|
||||
const handleClearRecent = useCallback(() => {
|
||||
clearRecentStickers();
|
||||
closeConfirmModal();
|
||||
}, [clearRecentStickers, closeConfirmModal]);
|
||||
|
||||
const stickersPerRow = IS_SINGLE_COLUMN_LAYOUT
|
||||
? Math.floor((windowSize.get().width - MOBILE_CONTAINER_PADDING) / (STICKER_SIZE_PICKER + STICKER_MARGIN))
|
||||
: STICKERS_PER_ROW_ON_DESKTOP;
|
||||
@ -60,6 +74,8 @@ const StickerSet: FC<OwnProps> = ({
|
||||
favoriteStickers ? new Set(favoriteStickers.map(({ id }) => id)) : undefined
|
||||
), [favoriteStickers]);
|
||||
|
||||
const isRecent = stickerSet.id === RECENT_SYMBOL_SET_ID;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@ -67,7 +83,12 @@ const StickerSet: FC<OwnProps> = ({
|
||||
id={`sticker-set-${index}`}
|
||||
className="symbol-set"
|
||||
>
|
||||
<p className="symbol-set-name">{stickerSet.title}</p>
|
||||
<div className="symbol-set-header">
|
||||
<p className="symbol-set-name">{stickerSet.title}</p>
|
||||
{isRecent && (
|
||||
<i className="symbol-set-remove icon-close" onClick={openConfirmModal} />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={buildClassName('symbol-set-container', transitionClassNames)}
|
||||
style={`height: ${height}px;`}
|
||||
@ -81,12 +102,25 @@ const StickerSet: FC<OwnProps> = ({
|
||||
noAnimate={!loadAndPlay}
|
||||
onClick={onStickerSelect}
|
||||
clickArg={sticker}
|
||||
onUnfaveClick={favoriteStickerIdsSet?.has(sticker.id) ? onStickerUnfave : undefined}
|
||||
onUnfaveClick={stickerSet.id === FAVORITE_SYMBOL_SET_ID && favoriteStickerIdsSet?.has(sticker.id)
|
||||
? onStickerUnfave : undefined}
|
||||
onFaveClick={!favoriteStickerIdsSet?.has(sticker.id) ? onStickerFave : undefined}
|
||||
onRemoveRecentClick={isRecent ? onStickerRemoveRecent : undefined}
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{isRecent && (
|
||||
<ConfirmDialog
|
||||
text={lang('ClearRecentEmoji')}
|
||||
isOpen={isConfirmModalOpen}
|
||||
onClose={closeConfirmModal}
|
||||
confirmHandler={handleClearRecent}
|
||||
confirmIsDestructive
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -82,6 +82,7 @@ const StickerTooltip: FC<OwnProps & StateProps> = ({
|
||||
onClick={onStickerSelect}
|
||||
clickArg={sticker}
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
/>
|
||||
))
|
||||
) : shouldRender ? (
|
||||
|
@ -147,18 +147,29 @@
|
||||
.symbol-set {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
&-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: rgba(var(--color-text-secondary-rgb), 0.75);
|
||||
}
|
||||
|
||||
&-name {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6875rem;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
padding-left: 0.5rem;
|
||||
color: rgba(var(--color-text-secondary-rgb), 0.75);
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: initial;
|
||||
unicode-bidi: plaintext;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&-remove {
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-container {
|
||||
|
@ -36,6 +36,7 @@ const StickerResult: FC<OwnProps> = ({
|
||||
onClick={onClick}
|
||||
clickArg={inlineResult}
|
||||
isSavedMessages={isSavedMessages}
|
||||
canViewSet
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -53,7 +53,7 @@ const ConfirmDialog: FC<OwnProps> = ({
|
||||
return (
|
||||
<Modal
|
||||
className="confirm"
|
||||
title={title}
|
||||
title={title || lang('Telegram')}
|
||||
header={header}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
|
@ -139,6 +139,8 @@ export const STICKER_SIZE_JOIN_REQUESTS = 140;
|
||||
export const STICKER_SIZE_INVITES = 140;
|
||||
export const RECENT_STICKERS_LIMIT = 20;
|
||||
export const NO_STICKER_SET_ID = 'NO_STICKER_SET';
|
||||
export const RECENT_SYMBOL_SET_ID = 'recent';
|
||||
export const FAVORITE_SYMBOL_SET_ID = 'favorite';
|
||||
|
||||
export const BASE_EMOJI_KEYWORD_LANG = 'en';
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||
import {
|
||||
addActionHandler, getActions, getGlobal, setGlobal,
|
||||
} from '../../index';
|
||||
|
||||
import { ApiSticker } from '../../../api/types';
|
||||
import { LangCode } from '../../../types';
|
||||
import { callApi } from '../../../api/gramjs';
|
||||
import { pause, throttle } from '../../../util/schedulers';
|
||||
import { onTickEnd, pause, throttle } from '../../../util/schedulers';
|
||||
import {
|
||||
updateStickerSets,
|
||||
updateStickerSet,
|
||||
@ -14,6 +16,7 @@ import {
|
||||
} from '../../reducers';
|
||||
import searchWords from '../../../util/searchWords';
|
||||
import { selectStickerSet } from '../../selectors';
|
||||
import { getTranslation } from '../../../util/langProvider';
|
||||
|
||||
const ADDED_SETS_THROTTLE = 200;
|
||||
const ADDED_SETS_THROTTLE_CHUNK = 10;
|
||||
@ -34,7 +37,7 @@ addActionHandler('loadAddedStickers', async (global, actions) => {
|
||||
|
||||
for (let i = 0; i < addedSetIds.length; i++) {
|
||||
const id = addedSetIds[i];
|
||||
if (cached[id].stickers) {
|
||||
if (cached[id]?.stickers) {
|
||||
continue; // Already loaded
|
||||
}
|
||||
actions.loadStickers({ stickerSetId: id });
|
||||
@ -89,13 +92,19 @@ addActionHandler('loadStickers', (global, actions, payload) => {
|
||||
if (!stickerSetAccessHash && !stickerSetShortName) {
|
||||
const stickerSet = selectStickerSet(global, stickerSetId);
|
||||
if (!stickerSet) {
|
||||
if (global.openedStickerSetShortName === stickerSetShortName) {
|
||||
setGlobal({
|
||||
...global,
|
||||
openedStickerSetShortName: undefined,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
stickerSetAccessHash = stickerSet.accessHash;
|
||||
}
|
||||
|
||||
void loadStickers(stickerSetId, stickerSetAccessHash, stickerSetShortName);
|
||||
void loadStickers(stickerSetId, stickerSetAccessHash!, stickerSetShortName);
|
||||
});
|
||||
|
||||
addActionHandler('loadAnimatedEmojis', () => {
|
||||
@ -147,6 +156,33 @@ addActionHandler('unfaveSticker', (global, actions, payload) => {
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('removeRecentSticker', async (global, action, payload) => {
|
||||
const { sticker } = payload!;
|
||||
|
||||
const result = await callApi('removeRecentSticker', { sticker });
|
||||
|
||||
if (!result) return;
|
||||
|
||||
loadRecentStickers();
|
||||
});
|
||||
|
||||
addActionHandler('clearRecentStickers', async (global) => {
|
||||
const result = await callApi('clearRecentStickers');
|
||||
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
setGlobal({
|
||||
...global,
|
||||
stickers: {
|
||||
...global.stickers,
|
||||
recent: {
|
||||
stickers: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('toggleStickerSet', (global, actions, payload) => {
|
||||
const { stickerSetId } = payload!;
|
||||
const stickerSet = selectStickerSet(global, stickerSetId);
|
||||
@ -284,14 +320,25 @@ async function loadStickers(stickerSetId: string, accessHash: string, stickerSet
|
||||
'fetchStickers',
|
||||
{ stickerSetShortName, stickerSetId, accessHash },
|
||||
);
|
||||
let global = getGlobal();
|
||||
|
||||
if (!stickerSet) {
|
||||
onTickEnd(() => {
|
||||
getActions().showNotification({
|
||||
message: getTranslation('StickerPack.ErrorNotFound'),
|
||||
});
|
||||
});
|
||||
if (global.openedStickerSetShortName === stickerSetShortName) {
|
||||
setGlobal({
|
||||
...global,
|
||||
openedStickerSetShortName: undefined,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { set, stickers, packs } = stickerSet;
|
||||
|
||||
let global = getGlobal();
|
||||
|
||||
global = updateStickerSet(global, set.id, { ...set, stickers, packs });
|
||||
|
||||
const currentEmoji = global.stickers.forEmoji.emoji;
|
||||
@ -396,13 +443,39 @@ addActionHandler('clearStickersForEmoji', (global) => {
|
||||
});
|
||||
|
||||
addActionHandler('openStickerSetShortName', (global, actions, payload) => {
|
||||
const { stickerSetShortName } = payload!;
|
||||
const { stickerSetShortName } = payload;
|
||||
return {
|
||||
...global,
|
||||
openedStickerSetShortName: stickerSetShortName,
|
||||
};
|
||||
});
|
||||
|
||||
addActionHandler('openStickerSet', async (global, actions, payload) => {
|
||||
const { sticker } = payload;
|
||||
|
||||
if (!selectStickerSet(global, sticker.stickerSetId)) {
|
||||
if (!sticker.stickerSetAccessHash) {
|
||||
actions.showNotification({
|
||||
message: getTranslation('StickerPack.ErrorNotFound'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await loadStickers(sticker.stickerSetId, sticker.stickerSetAccessHash);
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
const set = selectStickerSet(global, sticker.stickerSetId);
|
||||
if (!set?.shortName) {
|
||||
return;
|
||||
}
|
||||
|
||||
setGlobal({
|
||||
...global,
|
||||
openedStickerSetShortName: set.shortName,
|
||||
});
|
||||
});
|
||||
|
||||
async function searchStickers(query: string, hash?: string) {
|
||||
const result = await callApi('searchStickers', { query, hash });
|
||||
|
||||
|
@ -28,6 +28,22 @@ addActionHandler('apiUpdate', (global, actions, update) => {
|
||||
actions.loadFavoriteStickers();
|
||||
break;
|
||||
|
||||
case 'updateRecentStickers':
|
||||
actions.loadRecentStickers();
|
||||
break;
|
||||
|
||||
case 'updateStickerSets':
|
||||
actions.loadStickerSets();
|
||||
break;
|
||||
|
||||
case 'updateStickerSetsOrder':
|
||||
actions.reorderStickerSets({ order: update.order });
|
||||
break;
|
||||
|
||||
case 'updateSavedGifs':
|
||||
actions.loadSavedGifs();
|
||||
break;
|
||||
|
||||
case 'updatePrivacy':
|
||||
global.settings.privacy[update.key as ApiPrivacyKey] = update.rules;
|
||||
break;
|
||||
|
@ -161,7 +161,7 @@ addActionHandler('addRecentEmoji', (global, action, payload) => {
|
||||
});
|
||||
|
||||
addActionHandler('addRecentSticker', (global, action, payload) => {
|
||||
const { sticker } = payload!;
|
||||
const { sticker } = payload;
|
||||
const { recent } = global.stickers;
|
||||
if (!recent) {
|
||||
return {
|
||||
@ -191,6 +191,19 @@ addActionHandler('addRecentSticker', (global, action, payload) => {
|
||||
};
|
||||
});
|
||||
|
||||
addActionHandler('reorderStickerSets', (global, action, payload) => {
|
||||
const { order } = payload;
|
||||
return {
|
||||
...global,
|
||||
stickers: {
|
||||
...global.stickers,
|
||||
added: {
|
||||
setIds: order,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
addActionHandler('showNotification', (global, actions, payload) => {
|
||||
const notification = payload!;
|
||||
notification.localId = generateIdFor({});
|
||||
|
@ -728,6 +728,39 @@ export interface ActionPayloads {
|
||||
shouldSharePhoneNumber?: boolean;
|
||||
};
|
||||
|
||||
// Stickers
|
||||
addRecentSticker: {
|
||||
sticker: ApiSticker;
|
||||
};
|
||||
|
||||
removeRecentSticker: {
|
||||
sticker: ApiSticker;
|
||||
};
|
||||
|
||||
clearRecentStickers: {};
|
||||
|
||||
loadStickerSets: {};
|
||||
loadAddedStickers: {};
|
||||
loadRecentStickers: {};
|
||||
loadFavoriteStickers: {};
|
||||
loadFeaturedStickers: {};
|
||||
|
||||
reorderStickerSets: {
|
||||
order: string[];
|
||||
};
|
||||
|
||||
addNewStickerSet: {
|
||||
stickerSet: ApiStickerSet;
|
||||
};
|
||||
|
||||
openStickerSetShortName: {
|
||||
stickerSetShortName?: string;
|
||||
};
|
||||
|
||||
openStickerSet: {
|
||||
sticker: ApiSticker;
|
||||
};
|
||||
|
||||
// Bots
|
||||
clickBotInlineButton: {
|
||||
messageId: number;
|
||||
@ -835,7 +868,7 @@ export type NonTypedActionNames = (
|
||||
'init' | 'reset' | 'disconnect' | 'initApi' | 'sync' | 'saveSession' |
|
||||
'showNotification' | 'dismissNotification' | 'showDialog' | 'dismissDialog' |
|
||||
// ui
|
||||
'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'addRecentSticker' | 'toggleLeftColumn' |
|
||||
'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'toggleLeftColumn' |
|
||||
'toggleSafeLinkModal' | 'openHistoryCalendar' | 'closeHistoryCalendar' | 'disableContextMenuHint' |
|
||||
'setNewChatMembersDialogState' | 'disableHistoryAnimations' | 'setLeftColumnWidth' | 'resetLeftColumnWidth' |
|
||||
'openSeenByModal' | 'closeSeenByModal' | 'closeReactorListModal' | 'openReactorListModal' |
|
||||
@ -904,11 +937,9 @@ export type NonTypedActionNames = (
|
||||
'loadContentSettings' | 'updateContentSettings' |
|
||||
'loadCountryList' | 'ensureTimeFormat' | 'loadAppConfig' |
|
||||
// stickers & GIFs
|
||||
'loadStickerSets' | 'loadAddedStickers' | 'loadRecentStickers' | 'loadFavoriteStickers' | 'loadFeaturedStickers' |
|
||||
'loadStickers' | 'setStickerSearchQuery' | 'loadSavedGifs' | 'saveGif' | 'setGifSearchQuery' | 'searchMoreGifs' |
|
||||
'faveSticker' | 'unfaveSticker' | 'toggleStickerSet' | 'loadAnimatedEmojis' |
|
||||
'setStickerSearchQuery' | 'loadSavedGifs' | 'saveGif' | 'setGifSearchQuery' | 'searchMoreGifs' |
|
||||
'faveSticker' | 'unfaveSticker' | 'toggleStickerSet' | 'loadAnimatedEmojis' | 'loadStickers' |
|
||||
'loadStickersForEmoji' | 'clearStickersForEmoji' | 'loadEmojiKeywords' | 'loadGreetingStickers' |
|
||||
'openStickerSetShortName' |
|
||||
// bots
|
||||
'sendBotCommand' | 'loadTopInlineBots' | 'queryInlineBot' | 'sendInlineBotResult' |
|
||||
'resetInlineBot' | 'restartBot' | 'startBot' |
|
||||
|
@ -1095,6 +1095,8 @@ messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flag
|
||||
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
|
||||
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
|
||||
messages.getRecentStickers#9da9403b flags:# attached:flags.0?true hash:long = messages.RecentStickers;
|
||||
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
|
||||
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
|
||||
messages.getCommonChats#e40ca104 user_id:InputUser max_id:long limit:int = messages.Chats;
|
||||
messages.getWebPage#32ca8f91 url:string hash:int = WebPage;
|
||||
messages.toggleDialogPin#a731e257 flags:# pinned:flags.0?true peer:InputDialogPeer = Bool;
|
||||
|
@ -113,6 +113,8 @@
|
||||
"messages.getFeaturedStickers",
|
||||
"messages.readFeaturedStickers",
|
||||
"messages.getRecentStickers",
|
||||
"messages.saveRecentSticker",
|
||||
"messages.clearRecentStickers",
|
||||
"messages.getCommonChats",
|
||||
"messages.getWebPage",
|
||||
"messages.toggleDialogPin",
|
||||
|
Loading…
Reference in New Issue
Block a user