Folders Sidebar: Follow-up (#6501)
This commit is contained in:
parent
111c702d73
commit
3ba0a90d63
@ -29,6 +29,7 @@ type OwnProps = {
|
|||||||
noPlay?: boolean;
|
noPlay?: boolean;
|
||||||
noVideoOnMobile?: boolean;
|
noVideoOnMobile?: boolean;
|
||||||
loopLimit?: number;
|
loopLimit?: number;
|
||||||
|
shouldNotLoop?: boolean;
|
||||||
isSelectable?: boolean;
|
isSelectable?: boolean;
|
||||||
withSharedAnimation?: boolean;
|
withSharedAnimation?: boolean;
|
||||||
sharedCanvasRef?: ElementRef<HTMLCanvasElement>;
|
sharedCanvasRef?: ElementRef<HTMLCanvasElement>;
|
||||||
@ -55,6 +56,7 @@ const CustomEmoji: FC<OwnProps> = ({
|
|||||||
noPlay,
|
noPlay,
|
||||||
noVideoOnMobile,
|
noVideoOnMobile,
|
||||||
loopLimit,
|
loopLimit,
|
||||||
|
shouldNotLoop,
|
||||||
isSelectable,
|
isSelectable,
|
||||||
withSharedAnimation,
|
withSharedAnimation,
|
||||||
sharedCanvasRef,
|
sharedCanvasRef,
|
||||||
@ -146,7 +148,7 @@ const CustomEmoji: FC<OwnProps> = ({
|
|||||||
noVideoOnMobile={noVideoOnMobile}
|
noVideoOnMobile={noVideoOnMobile}
|
||||||
thumbClassName={styles.thumb}
|
thumbClassName={styles.thumb}
|
||||||
fullMediaClassName={styles.media}
|
fullMediaClassName={styles.media}
|
||||||
shouldLoop
|
shouldLoop={!shouldNotLoop}
|
||||||
loopLimit={loopLimit}
|
loopLimit={loopLimit}
|
||||||
shouldPreloadPreview={shouldPreloadPreview || noPlay || !canPlay}
|
shouldPreloadPreview={shouldPreloadPreview || noPlay || !canPlay}
|
||||||
forceOnHeavyAnimation={forceOnHeavyAnimation}
|
forceOnHeavyAnimation={forceOnHeavyAnimation}
|
||||||
|
|||||||
@ -71,9 +71,7 @@ type OwnProps = {
|
|||||||
onCustomEmojiSelect: (sticker: ApiSticker) => void;
|
onCustomEmojiSelect: (sticker: ApiSticker) => void;
|
||||||
onReactionSelect?: (reaction: ApiReactionWithPaid) => void;
|
onReactionSelect?: (reaction: ApiReactionWithPaid) => void;
|
||||||
onReactionContext?: (reaction: ApiReactionWithPaid) => void;
|
onReactionContext?: (reaction: ApiReactionWithPaid) => void;
|
||||||
onContextMenuOpen?: NoneToVoidFunction;
|
onDismiss?: NoneToVoidFunction;
|
||||||
onContextMenuClose?: NoneToVoidFunction;
|
|
||||||
onContextMenuClick?: NoneToVoidFunction;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -142,9 +140,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
|||||||
onCustomEmojiSelect,
|
onCustomEmojiSelect,
|
||||||
onReactionSelect,
|
onReactionSelect,
|
||||||
onReactionContext,
|
onReactionContext,
|
||||||
onContextMenuOpen,
|
onDismiss,
|
||||||
onContextMenuClose,
|
|
||||||
onContextMenuClick,
|
|
||||||
}) => {
|
}) => {
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
const headerRef = useRef<HTMLDivElement>();
|
const headerRef = useRef<HTMLDivElement>();
|
||||||
@ -466,9 +462,7 @@ const CustomEmojiPicker: FC<OwnProps & StateProps> = ({
|
|||||||
onReactionSelect={onReactionSelect}
|
onReactionSelect={onReactionSelect}
|
||||||
onReactionContext={onReactionContext}
|
onReactionContext={onReactionContext}
|
||||||
onStickerSelect={handleEmojiSelect}
|
onStickerSelect={handleEmojiSelect}
|
||||||
onContextMenuOpen={onContextMenuOpen}
|
onDismiss={onDismiss}
|
||||||
onContextMenuClose={onContextMenuClose}
|
|
||||||
onContextMenuClick={onContextMenuClick}
|
|
||||||
forcePlayback
|
forcePlayback
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,19 +11,19 @@ import styles from './FolderIcon.module.scss';
|
|||||||
|
|
||||||
const ICON_SIZE = 2.25 * REM;
|
const ICON_SIZE = 2.25 * REM;
|
||||||
|
|
||||||
const FolderIcon = (
|
type OwnProps = {
|
||||||
{
|
emoji?: string;
|
||||||
emoji,
|
customEmojiId?: string;
|
||||||
customEmojiId,
|
shouldAnimate?: boolean;
|
||||||
shouldAnimate,
|
};
|
||||||
}: {
|
|
||||||
emoji?: string;
|
const FolderIcon = ({
|
||||||
customEmojiId?: string;
|
emoji,
|
||||||
shouldAnimate?: boolean;
|
customEmojiId,
|
||||||
},
|
shouldAnimate,
|
||||||
) => {
|
}: OwnProps) => {
|
||||||
if (customEmojiId) {
|
if (customEmojiId) {
|
||||||
return <CustomEmoji documentId={customEmojiId} size={ICON_SIZE} noPlay={shouldAnimate} />;
|
return <CustomEmoji documentId={customEmojiId} size={ICON_SIZE} shouldNotLoop={!shouldAnimate} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emoji) {
|
if (!emoji) {
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sticker-locked {
|
.sticker-locked, .sticker-premium {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -55,30 +55,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
width: 1.25rem;
|
width: 1rem;
|
||||||
height: 1.25rem;
|
height: 1rem;
|
||||||
border-radius: 50%;
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
opacity: 0.75;
|
|
||||||
background: var(--premium-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sticker-premium {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
background: var(--premium-gradient);
|
background: var(--premium-gradient);
|
||||||
@ -135,8 +116,8 @@
|
|||||||
top: -0.125rem;
|
top: -0.125rem;
|
||||||
right: -0.125rem;
|
right: -0.125rem;
|
||||||
|
|
||||||
width: 1.25rem;
|
width: 1rem;
|
||||||
height: 1.25rem;
|
height: 1rem;
|
||||||
padding: 0.125rem;
|
padding: 0.125rem;
|
||||||
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@ -45,18 +45,17 @@ type OwnProps<T> = {
|
|||||||
sharedCanvasRef?: ElementRef<HTMLCanvasElement>;
|
sharedCanvasRef?: ElementRef<HTMLCanvasElement>;
|
||||||
withTranslucentThumb?: boolean;
|
withTranslucentThumb?: boolean;
|
||||||
forcePlayback?: boolean;
|
forcePlayback?: boolean;
|
||||||
|
isEffectEmoji?: boolean;
|
||||||
|
noShowPremium?: boolean;
|
||||||
|
noIcons?: boolean;
|
||||||
|
clickArg: T;
|
||||||
|
onClick?: (arg: OwnProps<T>['clickArg'], isSilent?: boolean, shouldSchedule?: boolean) => void;
|
||||||
observeIntersection: ObserveFn;
|
observeIntersection: ObserveFn;
|
||||||
observeIntersectionForShowing?: ObserveFn;
|
observeIntersectionForShowing?: ObserveFn;
|
||||||
noShowPremium?: boolean;
|
|
||||||
onClick?: (arg: OwnProps<T>['clickArg'], isSilent?: boolean, shouldSchedule?: boolean) => void;
|
|
||||||
clickArg: T;
|
|
||||||
onFaveClick?: (sticker: ApiSticker) => void;
|
onFaveClick?: (sticker: ApiSticker) => void;
|
||||||
onUnfaveClick?: (sticker: ApiSticker) => void;
|
onUnfaveClick?: (sticker: ApiSticker) => void;
|
||||||
onRemoveRecentClick?: (sticker: ApiSticker) => void;
|
onRemoveRecentClick?: (sticker: ApiSticker) => void;
|
||||||
onContextMenuOpen?: NoneToVoidFunction;
|
onDismiss?: NoneToVoidFunction;
|
||||||
onContextMenuClose?: NoneToVoidFunction;
|
|
||||||
onContextMenuClick?: NoneToVoidFunction;
|
|
||||||
isEffectEmoji?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const contentForStatusMenuContext = [
|
const contentForStatusMenuContext = [
|
||||||
@ -77,8 +76,6 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
isSavedMessages,
|
isSavedMessages,
|
||||||
isStatusPicker,
|
isStatusPicker,
|
||||||
canViewSet,
|
canViewSet,
|
||||||
observeIntersection,
|
|
||||||
observeIntersectionForShowing,
|
|
||||||
isSelected,
|
isSelected,
|
||||||
isCurrentUserPremium,
|
isCurrentUserPremium,
|
||||||
shouldIgnorePremium,
|
shouldIgnorePremium,
|
||||||
@ -86,15 +83,16 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
sharedCanvasRef,
|
sharedCanvasRef,
|
||||||
withTranslucentThumb,
|
withTranslucentThumb,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
onClick,
|
isEffectEmoji,
|
||||||
|
noIcons,
|
||||||
clickArg,
|
clickArg,
|
||||||
|
onClick,
|
||||||
|
observeIntersection,
|
||||||
|
observeIntersectionForShowing,
|
||||||
onFaveClick,
|
onFaveClick,
|
||||||
onUnfaveClick,
|
onUnfaveClick,
|
||||||
onRemoveRecentClick,
|
onRemoveRecentClick,
|
||||||
onContextMenuOpen,
|
onDismiss,
|
||||||
onContextMenuClose,
|
|
||||||
onContextMenuClick,
|
|
||||||
isEffectEmoji,
|
|
||||||
}: OwnProps<T>) => {
|
}: OwnProps<T>) => {
|
||||||
const { openStickerSet, openPremiumModal, setEmojiStatus } = getActions();
|
const { openStickerSet, openPremiumModal, setEmojiStatus } = getActions();
|
||||||
const ref = useRef<HTMLDivElement>();
|
const ref = useRef<HTMLDivElement>();
|
||||||
@ -109,6 +107,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
|
|
||||||
const isPremium = !sticker.isFree || sticker.hasEffect;
|
const isPremium = !sticker.isFree || sticker.hasEffect;
|
||||||
const isCustomEmoji = sticker.isCustomEmoji || isEffectEmoji;
|
const isCustomEmoji = sticker.isCustomEmoji || isEffectEmoji;
|
||||||
|
const isPremiumSticker = !isCustomEmoji && isPremium;
|
||||||
const isLocked = !isCurrentUserPremium && isPremium && !shouldIgnorePremium;
|
const isLocked = !isCurrentUserPremium && isPremium && !shouldIgnorePremium;
|
||||||
|
|
||||||
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
const isIntersecting = useIsIntersecting(ref, observeIntersection);
|
||||||
@ -131,14 +130,6 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
});
|
});
|
||||||
const getLayout = useLastCallback(() => ({ withPortal: isStatusPicker, shouldAvoidNegativePosition: true }));
|
const getLayout = useLastCallback(() => ({ withPortal: isStatusPicker, shouldAvoidNegativePosition: true }));
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isContextMenuOpen) {
|
|
||||||
onContextMenuOpen?.();
|
|
||||||
} else {
|
|
||||||
onContextMenuClose?.();
|
|
||||||
}
|
|
||||||
}, [isContextMenuOpen, onContextMenuClose, onContextMenuOpen]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isIntersecting) handleContextMenuClose();
|
if (!isIntersecting) handleContextMenuClose();
|
||||||
}, [handleContextMenuClose, isIntersecting]);
|
}, [handleContextMenuClose, isIntersecting]);
|
||||||
@ -153,7 +144,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
} else {
|
} else {
|
||||||
openPremiumModal({ initialSection: 'premium_stickers' });
|
openPremiumModal({ initialSection: 'premium_stickers' });
|
||||||
}
|
}
|
||||||
onContextMenuClose?.();
|
onDismiss?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onClick?.(clickArg);
|
onClick?.(clickArg);
|
||||||
@ -200,7 +191,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
handleContextMenuClose();
|
handleContextMenuClose();
|
||||||
onContextMenuClick?.();
|
onDismiss?.();
|
||||||
setEmojiStatus({
|
setEmojiStatus({
|
||||||
emojiStatus: { type: 'regular', documentId: sticker.id, until: getServerTime() + duration },
|
emojiStatus: { type: 'regular', documentId: sticker.id, until: getServerTime() + duration },
|
||||||
});
|
});
|
||||||
@ -310,14 +301,14 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
|||||||
forceAlways={forcePlayback}
|
forceAlways={forcePlayback}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!noShowPremium && isLocked && (
|
{!noIcons && !noShowPremium && isLocked && (
|
||||||
<div
|
<div
|
||||||
className="sticker-locked"
|
className="sticker-locked"
|
||||||
>
|
>
|
||||||
<Icon name="lock-badge" />
|
<Icon name="lock-badge" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!noShowPremium && isPremium && !isLocked && (
|
{!noIcons && !noShowPremium && isPremiumSticker && !isLocked && (
|
||||||
<div className="sticker-premium">
|
<div className="sticker-premium">
|
||||||
<Icon name="star" />
|
<Icon name="star" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
|
||||||
import {
|
import {
|
||||||
memo, useEffect, useMemo, useRef, useState,
|
memo, useEffect, useMemo, useRef, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
@ -74,9 +73,7 @@ type OwnProps = {
|
|||||||
onStickerUnfave?: (sticker: ApiSticker) => void;
|
onStickerUnfave?: (sticker: ApiSticker) => void;
|
||||||
onStickerFave?: (sticker: ApiSticker) => void;
|
onStickerFave?: (sticker: ApiSticker) => void;
|
||||||
onStickerRemoveRecent?: (sticker: ApiSticker) => void;
|
onStickerRemoveRecent?: (sticker: ApiSticker) => void;
|
||||||
onContextMenuOpen?: NoneToVoidFunction;
|
onDismiss?: NoneToVoidFunction;
|
||||||
onContextMenuClose?: NoneToVoidFunction;
|
|
||||||
onContextMenuClick?: NoneToVoidFunction;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -89,7 +86,7 @@ const ITEMS_MINI_MOBILE_PER_ROW_FALLBACK = 6;
|
|||||||
const MOBILE_WIDTH_THRESHOLD_PX = 440;
|
const MOBILE_WIDTH_THRESHOLD_PX = 440;
|
||||||
const MINI_MOBILE_WIDTH_THRESHOLD_PX = 362;
|
const MINI_MOBILE_WIDTH_THRESHOLD_PX = 362;
|
||||||
|
|
||||||
const StickerSet: FC<OwnProps & StateProps> = ({
|
const StickerSet = ({
|
||||||
stickerSet,
|
stickerSet,
|
||||||
loadAndPlay,
|
loadAndPlay,
|
||||||
index,
|
index,
|
||||||
@ -110,6 +107,7 @@ const StickerSet: FC<OwnProps & StateProps> = ({
|
|||||||
isTranslucent,
|
isTranslucent,
|
||||||
noContextMenus,
|
noContextMenus,
|
||||||
forcePlayback,
|
forcePlayback,
|
||||||
|
collectibleStatuses,
|
||||||
observeIntersection,
|
observeIntersection,
|
||||||
observeIntersectionForPlayingItems,
|
observeIntersectionForPlayingItems,
|
||||||
observeIntersectionForShowingItems,
|
observeIntersectionForShowingItems,
|
||||||
@ -119,11 +117,8 @@ const StickerSet: FC<OwnProps & StateProps> = ({
|
|||||||
onStickerUnfave,
|
onStickerUnfave,
|
||||||
onStickerFave,
|
onStickerFave,
|
||||||
onStickerRemoveRecent,
|
onStickerRemoveRecent,
|
||||||
onContextMenuOpen,
|
onDismiss,
|
||||||
onContextMenuClose,
|
}: OwnProps & StateProps) => {
|
||||||
onContextMenuClick,
|
|
||||||
collectibleStatuses,
|
|
||||||
}) => {
|
|
||||||
const {
|
const {
|
||||||
clearRecentStickers,
|
clearRecentStickers,
|
||||||
clearRecentCustomEmoji,
|
clearRecentCustomEmoji,
|
||||||
@ -401,13 +396,12 @@ const StickerSet: FC<OwnProps & StateProps> = ({
|
|||||||
withTranslucentThumb={isTranslucent}
|
withTranslucentThumb={isTranslucent}
|
||||||
onClick={onStickerSelect}
|
onClick={onStickerSelect}
|
||||||
clickArg={sticker}
|
clickArg={sticker}
|
||||||
|
noIcons={isEmoji && !isRecent}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
onUnfaveClick={isFavorite && favoriteStickerIdsSet?.has(sticker.id) ? onStickerUnfave : undefined}
|
onUnfaveClick={isFavorite && favoriteStickerIdsSet?.has(sticker.id) ? onStickerUnfave : undefined}
|
||||||
onFaveClick={!favoriteStickerIdsSet?.has(sticker.id) ? onStickerFave : undefined}
|
onFaveClick={!favoriteStickerIdsSet?.has(sticker.id) ? onStickerFave : undefined}
|
||||||
onRemoveRecentClick={isRecent ? onStickerRemoveRecent : undefined}
|
onRemoveRecentClick={isRecent ? onStickerRemoveRecent : undefined}
|
||||||
onContextMenuOpen={onContextMenuOpen}
|
onDismiss={onDismiss}
|
||||||
onContextMenuClose={onContextMenuClose}
|
|
||||||
onContextMenuClick={onContextMenuClick}
|
|
||||||
forcePlayback={forcePlayback}
|
forcePlayback={forcePlayback}
|
||||||
isEffectEmoji={stickerSet.id === EFFECT_EMOJIS_SET_ID}
|
isEffectEmoji={stickerSet.id === EFFECT_EMOJIS_SET_ID}
|
||||||
noShowPremium={isCurrentUserPremium
|
noShowPremium={isCurrentUserPremium
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.foldersSidebar {
|
.foldersSidebar {
|
||||||
width: var(--tabs-sidebar-width);
|
width: var(--folders-sidebar-width);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--color-background-sidebar);
|
background: var(--color-background-sidebar);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { getActions, getGlobal, withGlobal } from '../../global';
|
|||||||
import type { TabState } from '../../global/types';
|
import type { TabState } from '../../global/types';
|
||||||
import { ApiMediaFormat } from '../../api/types';
|
import { ApiMediaFormat } from '../../api/types';
|
||||||
|
|
||||||
import { TABS_POSITION_LEFT } from '../../config';
|
import { FOLDERS_POSITION_LEFT } from '../../config';
|
||||||
import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting
|
import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting
|
||||||
import { selectAreFoldersPresent, selectIsRightColumnShown, selectTabState } from '../../global/selectors';
|
import { selectAreFoldersPresent, selectIsRightColumnShown, selectTabState } from '../../global/selectors';
|
||||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||||
@ -179,14 +179,14 @@ export default withGlobal<OwnProps>(
|
|||||||
(global, { isMobile }): Complete<StateProps> => {
|
(global, { isMobile }): Complete<StateProps> => {
|
||||||
const tabState = selectTabState(global);
|
const tabState = selectTabState(global);
|
||||||
|
|
||||||
const { tabsPosition } = selectSharedSettings(global);
|
const { foldersPosition } = selectSharedSettings(global);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations,
|
||||||
uiReadyState: tabState.uiReadyState,
|
uiReadyState: tabState.uiReadyState,
|
||||||
isRightColumnShown: selectIsRightColumnShown(global, isMobile),
|
isRightColumnShown: selectIsRightColumnShown(global, isMobile),
|
||||||
leftColumnWidth: global.leftColumnWidth,
|
leftColumnWidth: global.leftColumnWidth,
|
||||||
isFoldersSidebarShown: tabsPosition === TABS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global),
|
isFoldersSidebarShown: foldersPosition === FOLDERS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(UiLoader);
|
)(UiLoader);
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.is-tauri.is-macos #Main:not(.is-fullscreen):not(.tabs-sidebar-visible) &:not(#TopicListHeader) {
|
body.is-tauri.is-macos #Main:not(.is-fullscreen):not(.folders-sidebar-visible) &:not(#TopicListHeader) {
|
||||||
padding-left: var(--window-controls-width);
|
padding-left: var(--window-controls-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
.menuContent {
|
.menuContent {
|
||||||
--offset-y: 3.25rem !important;
|
--offset-y: 3.25rem !important;
|
||||||
--offset-x: auto !important;
|
--offset-x: var(--folders-sidebar-width) !important;
|
||||||
--color-text: var(--color-primary);
|
--color-text: var(--color-primary);
|
||||||
--border-radius-default: 1.25rem;
|
--border-radius-default: 1.25rem;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { ElementRef, FC } from '../../../lib/teact/teact';
|
import type { ElementRef } from '../../../lib/teact/teact';
|
||||||
import {
|
import {
|
||||||
memo, useCallback, useEffect, useRef,
|
memo, useCallback, useEffect, useRef,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
@ -8,8 +8,6 @@ import type { ApiSticker } from '../../../api/types';
|
|||||||
|
|
||||||
import { selectIsContextMenuTranslucent } from '../../../global/selectors';
|
import { selectIsContextMenuTranslucent } from '../../../global/selectors';
|
||||||
|
|
||||||
import useFlag from '../../../hooks/useFlag';
|
|
||||||
|
|
||||||
import CustomEmojiPicker from '../../common/CustomEmojiPicker';
|
import CustomEmojiPicker from '../../common/CustomEmojiPicker';
|
||||||
import Menu from '../../ui/Menu';
|
import Menu from '../../ui/Menu';
|
||||||
import Portal from '../../ui/Portal';
|
import Portal from '../../ui/Portal';
|
||||||
@ -28,18 +26,17 @@ interface StateProps {
|
|||||||
isTranslucent?: boolean;
|
isTranslucent?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
const StatusPickerMenu = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
statusButtonRef,
|
statusButtonRef,
|
||||||
areFeaturedStickersLoaded,
|
areFeaturedStickersLoaded,
|
||||||
isTranslucent,
|
isTranslucent,
|
||||||
onEmojiStatusSelect,
|
onEmojiStatusSelect,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}: OwnProps & StateProps) => {
|
||||||
const { loadFeaturedEmojiStickers } = getActions();
|
const { loadFeaturedEmojiStickers } = getActions();
|
||||||
|
|
||||||
const transformOriginX = useRef<number>();
|
const transformOriginX = useRef<number>(0);
|
||||||
const [isContextMenuShown, markContextMenuShown, unmarkContextMenuShown] = useFlag();
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
transformOriginX.current = statusButtonRef.current!.getBoundingClientRect().right;
|
transformOriginX.current = statusButtonRef.current!.getBoundingClientRect().right;
|
||||||
}, [isOpen, statusButtonRef]);
|
}, [isOpen, statusButtonRef]);
|
||||||
@ -60,11 +57,10 @@ const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
|||||||
<Menu
|
<Menu
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
noCompact
|
noCompact
|
||||||
positionX="right"
|
positionX="left"
|
||||||
bubbleClassName={styles.menuContent}
|
bubbleClassName={styles.menuContent}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
transformOriginX={transformOriginX.current}
|
transformOriginX={transformOriginX.current}
|
||||||
noCloseOnBackdrop={isContextMenuShown}
|
|
||||||
>
|
>
|
||||||
<CustomEmojiPicker
|
<CustomEmojiPicker
|
||||||
idPrefix="status-emoji-set-"
|
idPrefix="status-emoji-set-"
|
||||||
@ -72,10 +68,8 @@ const StatusPickerMenu: FC<OwnProps & StateProps> = ({
|
|||||||
isHidden={!isOpen}
|
isHidden={!isOpen}
|
||||||
isStatusPicker
|
isStatusPicker
|
||||||
isTranslucent={isTranslucent}
|
isTranslucent={isTranslucent}
|
||||||
onContextMenuOpen={markContextMenuShown}
|
onDismiss={onClose}
|
||||||
onContextMenuClose={unmarkContextMenuShown}
|
|
||||||
onCustomEmojiSelect={handleEmojiSelect}
|
onCustomEmojiSelect={handleEmojiSelect}
|
||||||
onContextMenuClick={onClose}
|
|
||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|||||||
@ -47,8 +47,7 @@ const FolderIconPickerMenu = ({
|
|||||||
loadAndPlay={isOpen}
|
loadAndPlay={isOpen}
|
||||||
isHidden={!isOpen}
|
isHidden={!isOpen}
|
||||||
onCustomEmojiSelect={(emoji) => handleEmojiSelect(emoji)}
|
onCustomEmojiSelect={(emoji) => handleEmojiSelect(emoji)}
|
||||||
onContextMenuClick={onClose}
|
onDismiss={onClose}
|
||||||
onContextMenuClose={onClose}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import type { FC } from '../../../../lib/teact/teact';
|
|
||||||
import {
|
import {
|
||||||
memo, useCallback, useEffect, useMemo, useState,
|
memo, useCallback, useEffect, useMemo, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../../global';
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type { ApiChatFolder } from '../../../../api/types';
|
import type { ApiChatFolder } from '../../../../api/types';
|
||||||
import type { TabsPosition } from '../../../../types';
|
import type { FoldersPosition } from '../../../../types';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
import {
|
||||||
|
ALL_FOLDER_ID, FOLDERS_POSITION_LEFT, FOLDERS_POSITION_TOP, STICKER_SIZE_FOLDER_SETTINGS,
|
||||||
|
} from '../../../../config';
|
||||||
import { getFolderDescriptionText } from '../../../../global/helpers';
|
import { getFolderDescriptionText } from '../../../../global/helpers';
|
||||||
import { selectIsCurrentUserPremium } from '../../../../global/selectors';
|
import { selectIsCurrentUserPremium } from '../../../../global/selectors';
|
||||||
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
import { selectCurrentLimit } from '../../../../global/selectors/limits';
|
||||||
@ -36,10 +37,10 @@ import RadioGroup from '../../../ui/RadioGroup';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
|
isMobile?: boolean;
|
||||||
onCreateFolder: () => void;
|
onCreateFolder: () => void;
|
||||||
onEditFolder: (folder: ApiChatFolder) => void;
|
onEditFolder: (folder: ApiChatFolder) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
isMobile?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -49,7 +50,7 @@ type StateProps = {
|
|||||||
maxFolders: number;
|
maxFolders: number;
|
||||||
isPremium?: boolean;
|
isPremium?: boolean;
|
||||||
areTagsEnabled?: boolean;
|
areTagsEnabled?: boolean;
|
||||||
tabsPosition: TabsPosition;
|
foldersPosition: FoldersPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SortState = {
|
type SortState = {
|
||||||
@ -61,20 +62,20 @@ type SortState = {
|
|||||||
const FOLDER_HEIGHT_PX = 56;
|
const FOLDER_HEIGHT_PX = 56;
|
||||||
const runThrottledForLoadRecommended = throttle((cb) => cb(), 60000, true);
|
const runThrottledForLoadRecommended = throttle((cb) => cb(), 60000, true);
|
||||||
|
|
||||||
const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
const SettingsFoldersMain = ({
|
||||||
isActive,
|
isActive,
|
||||||
onCreateFolder,
|
|
||||||
onEditFolder,
|
|
||||||
onReset,
|
|
||||||
folderIds,
|
|
||||||
foldersById,
|
foldersById,
|
||||||
isPremium,
|
isPremium,
|
||||||
recommendedChatFolders,
|
recommendedChatFolders,
|
||||||
maxFolders,
|
maxFolders,
|
||||||
areTagsEnabled,
|
areTagsEnabled,
|
||||||
tabsPosition,
|
foldersPosition,
|
||||||
isMobile,
|
isMobile,
|
||||||
}) => {
|
onCreateFolder,
|
||||||
|
onEditFolder,
|
||||||
|
onReset,
|
||||||
|
folderIds,
|
||||||
|
}: OwnProps & StateProps) => {
|
||||||
const {
|
const {
|
||||||
loadRecommendedChatFolders,
|
loadRecommendedChatFolders,
|
||||||
addChatFolder,
|
addChatFolder,
|
||||||
@ -215,8 +216,8 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}, [sortChatFolders]);
|
}, [sortChatFolders]);
|
||||||
|
|
||||||
const handleTabsPositionChange = useLastCallback((value: string) => {
|
const handleFoldersPositionChange = useLastCallback((value: string) => {
|
||||||
setSharedSettingOption({ tabsPosition: value as TabsPosition });
|
setSharedSettingOption({ foldersPosition: value as FoldersPosition });
|
||||||
});
|
});
|
||||||
|
|
||||||
const canCreateNewFolder = useMemo(() => {
|
const canCreateNewFolder = useMemo(() => {
|
||||||
@ -430,13 +431,13 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
|||||||
name="tabsPosition"
|
name="tabsPosition"
|
||||||
options={[{
|
options={[{
|
||||||
label: lang('TabsPositionLeft'),
|
label: lang('TabsPositionLeft'),
|
||||||
value: 'left',
|
value: FOLDERS_POSITION_LEFT,
|
||||||
}, {
|
}, {
|
||||||
label: lang('TabsPositionTop'),
|
label: lang('TabsPositionTop'),
|
||||||
value: 'top',
|
value: FOLDERS_POSITION_TOP,
|
||||||
}]}
|
}]}
|
||||||
selected={tabsPosition}
|
selected={foldersPosition}
|
||||||
onChange={handleTabsPositionChange}
|
onChange={handleFoldersPositionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -460,7 +461,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
recommendedChatFolders,
|
recommendedChatFolders,
|
||||||
maxFolders: selectCurrentLimit(global, 'dialogFilters'),
|
maxFolders: selectCurrentLimit(global, 'dialogFilters'),
|
||||||
areTagsEnabled,
|
areTagsEnabled,
|
||||||
tabsPosition: global.sharedState.settings.tabsPosition,
|
foldersPosition: global.sharedState.settings.foldersPosition,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(SettingsFoldersMain));
|
)(SettingsFoldersMain));
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
width: var(--tabs-sidebar-width);
|
width: var(--folders-sidebar-width);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
background-color: var(--color-background-sidebar);
|
background-color: var(--color-background-sidebar);
|
||||||
@ -78,7 +78,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menuButton {
|
.menuButton {
|
||||||
width: var(--tabs-sidebar-width);
|
width: var(--folders-sidebar-width);
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { memo, useEffect, useMemo, useRef } from '@teact';
|
import { memo, useEffect, useMemo, useRef } from '@teact';
|
||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { ApiChatFolder, ApiChatlistExportedInvite, ApiMessageEntityCustomEmoji } from '../../api/types';
|
import type { ApiChatFolder, ApiChatlistExportedInvite } from '../../api/types';
|
||||||
import { LeftColumnContent, SettingsScreens } from '../../types';
|
import { LeftColumnContent, SettingsScreens } from '../../types';
|
||||||
|
|
||||||
import { selectTabState } from '../../global/selectors';
|
import { selectTabState } from '../../global/selectors';
|
||||||
@ -15,7 +15,6 @@ import useLang from '../../hooks/useLang';
|
|||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
import useScrolledState from '../../hooks/useScrolledState';
|
import useScrolledState from '../../hooks/useScrolledState';
|
||||||
|
|
||||||
import FolderIcon from '../common/FolderIcon';
|
|
||||||
import MainMenuDropdown from '../common/MainMenuDropdown';
|
import MainMenuDropdown from '../common/MainMenuDropdown';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import Folder from '../ui/Folder';
|
import Folder from '../ui/Folder';
|
||||||
@ -158,14 +157,8 @@ const FoldersSidebar = ({
|
|||||||
clickArg={i}
|
clickArg={i}
|
||||||
contextActions={tab.contextActions}
|
contextActions={tab.contextActions}
|
||||||
contextRootElementSelector="#FoldersSidebar"
|
contextRootElementSelector="#FoldersSidebar"
|
||||||
|
icon={tab.emoticon}
|
||||||
className={styles.tab}
|
className={styles.tab}
|
||||||
icon={(
|
|
||||||
<FolderIcon
|
|
||||||
emoji={(tab.emoticon as string)}
|
|
||||||
customEmojiId={(tab.emoticon as ApiMessageEntityCustomEmoji)?.documentId}
|
|
||||||
shouldAnimate={tab.noTitleAnimations}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
&.tabs-sidebar-visible {
|
&.folders-sidebar-visible {
|
||||||
grid-template-columns: auto auto 1fr;
|
grid-template-columns: auto auto 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,9 +218,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 925px) {
|
@media (max-width: 925px) {
|
||||||
.tabs-sidebar-visible {
|
.folders-sidebar-visible {
|
||||||
#LeftColumn {
|
#LeftColumn {
|
||||||
left: var(--tabs-sidebar-width) !important;
|
left: var(--folders-sidebar-width) !important;
|
||||||
width: 21.5rem !important;
|
width: 21.5rem !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { getActions, getGlobal, withGlobal } from '../../global';
|
|||||||
import type { ApiChatFolder, ApiLimitTypeWithModal, ApiUser } from '../../api/types';
|
import type { ApiChatFolder, ApiLimitTypeWithModal, ApiUser } from '../../api/types';
|
||||||
import type { TabState } from '../../global/types';
|
import type { TabState } from '../../global/types';
|
||||||
|
|
||||||
import { BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, TABS_POSITION_LEFT } from '../../config';
|
import { BASE_EMOJI_KEYWORD_LANG, DEBUG, FOLDERS_POSITION_LEFT, INACTIVE_MARKER } from '../../config';
|
||||||
import { requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
import { requestNextMutation } from '../../lib/fasterdom/fasterdom';
|
||||||
import {
|
import {
|
||||||
selectAreFoldersPresent,
|
selectAreFoldersPresent,
|
||||||
@ -522,7 +522,7 @@ const Main = ({
|
|||||||
isNarrowMessageList && 'narrow-message-list',
|
isNarrowMessageList && 'narrow-message-list',
|
||||||
shouldSkipHistoryAnimations && 'history-animation-disabled',
|
shouldSkipHistoryAnimations && 'history-animation-disabled',
|
||||||
isFullscreen && 'is-fullscreen',
|
isFullscreen && 'is-fullscreen',
|
||||||
isFoldersSidebarShown && 'tabs-sidebar-visible',
|
isFoldersSidebarShown && 'folders-sidebar-visible',
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBlur = useLastCallback(() => {
|
const handleBlur = useLastCallback(() => {
|
||||||
@ -643,7 +643,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
deleteFolderDialogModal,
|
deleteFolderDialogModal,
|
||||||
} = selectTabState(global);
|
} = selectTabState(global);
|
||||||
|
|
||||||
const { wasTimeFormatSetManually, tabsPosition } = selectSharedSettings(global);
|
const { wasTimeFormatSetManually, foldersPosition } = selectSharedSettings(global);
|
||||||
|
|
||||||
const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId);
|
const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId);
|
||||||
const gameTitle = gameMessage?.content.game?.title;
|
const gameTitle = gameMessage?.content.game?.title;
|
||||||
@ -700,7 +700,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isSynced: global.isSynced,
|
isSynced: global.isSynced,
|
||||||
isAccountFrozen,
|
isAccountFrozen,
|
||||||
isAppConfigLoaded: global.isAppConfigLoaded,
|
isAppConfigLoaded: global.isAppConfigLoaded,
|
||||||
isFoldersSidebarShown: tabsPosition === TABS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global),
|
isFoldersSidebarShown: foldersPosition === FOLDERS_POSITION_LEFT && !isMobile && selectAreFoldersPresent(global),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Main));
|
)(Main));
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
width: var(--tabs-sidebar-width);
|
width: var(--folders-sidebar-width);
|
||||||
min-height: 4.5rem;
|
min-height: 4.5rem;
|
||||||
padding-right: 0.25rem;
|
padding-right: 0.25rem;
|
||||||
padding-left: 0.375rem;
|
padding-left: 0.375rem;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { TeactNode } from '../../lib/teact/teact';
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
import { useRef } from '../../lib/teact/teact';
|
import { useRef } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import type { ApiMessageEntityCustomEmoji } from '../../api/types';
|
||||||
import type { MenuItemContextAction } from './ListItem';
|
import type { MenuItemContextAction } from './ListItem';
|
||||||
|
|
||||||
import { MouseButton } from '../../util/browser/windowEnvironment';
|
import { MouseButton } from '../../util/browser/windowEnvironment';
|
||||||
@ -8,8 +9,10 @@ import buildClassName from '../../util/buildClassName';
|
|||||||
|
|
||||||
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
import useContextMenuHandlers from '../../hooks/useContextMenuHandlers';
|
||||||
import { useFastClick } from '../../hooks/useFastClick';
|
import { useFastClick } from '../../hooks/useFastClick';
|
||||||
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useLastCallback from '../../hooks/useLastCallback';
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
|
import FolderIcon from '../common/FolderIcon';
|
||||||
import Icon from '../common/icons/Icon';
|
import Icon from '../common/icons/Icon';
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
import MenuItem from './MenuItem';
|
import MenuItem from './MenuItem';
|
||||||
@ -26,7 +29,7 @@ type OwnProps = {
|
|||||||
isBadgeActive?: boolean;
|
isBadgeActive?: boolean;
|
||||||
contextActions?: MenuItemContextAction[];
|
contextActions?: MenuItemContextAction[];
|
||||||
contextRootElementSelector?: string;
|
contextRootElementSelector?: string;
|
||||||
icon?: TeactNode;
|
icon?: string | ApiMessageEntityCustomEmoji;
|
||||||
clickArg?: number;
|
clickArg?: number;
|
||||||
onClick?: (arg: number) => void;
|
onClick?: (arg: number) => void;
|
||||||
};
|
};
|
||||||
@ -45,6 +48,7 @@ const Folder = ({
|
|||||||
onClick,
|
onClick,
|
||||||
}: OwnProps) => {
|
}: OwnProps) => {
|
||||||
const folderRef = useRef<HTMLDivElement>();
|
const folderRef = useRef<HTMLDivElement>();
|
||||||
|
const [isHovering, markHovering, unmarkHovering] = useFlag();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
contextMenuAnchor, handleContextMenu, handleBeforeContextMenu, handleContextMenuClose,
|
contextMenuAnchor, handleContextMenu, handleBeforeContextMenu, handleContextMenuClose,
|
||||||
@ -78,10 +82,16 @@ const Folder = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
|
onMouseEnter={markHovering}
|
||||||
|
onMouseLeave={unmarkHovering}
|
||||||
ref={folderRef}
|
ref={folderRef}
|
||||||
>
|
>
|
||||||
<div className={styles.icon}>
|
<div className={styles.icon}>
|
||||||
{icon}
|
<FolderIcon
|
||||||
|
emoji={typeof icon === 'string' ? icon : undefined}
|
||||||
|
customEmojiId={typeof icon === 'object' ? icon.documentId : undefined}
|
||||||
|
shouldAnimate={isHovering}
|
||||||
|
/>
|
||||||
{Boolean(badgeCount) && (
|
{Boolean(badgeCount) && (
|
||||||
<span className={buildClassName(styles.badge, isBadgeActive && styles.badgeActive)}>{badgeCount}</span>
|
<span className={buildClassName(styles.badge, isBadgeActive && styles.badgeActive)}>{badgeCount}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -141,9 +141,9 @@ export const DEFAULT_MESSAGE_TEXT_SIZE_PX = 16;
|
|||||||
export const IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX = 17;
|
export const IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX = 17;
|
||||||
export const MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX = 15;
|
export const MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX = 15;
|
||||||
|
|
||||||
export const TABS_POSITION_TOP = 'top';
|
export const FOLDERS_POSITION_TOP = 'top';
|
||||||
export const TABS_POSITION_LEFT = 'left';
|
export const FOLDERS_POSITION_LEFT = 'left';
|
||||||
export const TABS_POSITION_DEFAULT = TABS_POSITION_TOP;
|
export const FOLDERS_POSITION_DEFAULT = FOLDERS_POSITION_TOP;
|
||||||
|
|
||||||
export const PREVIEW_AVATAR_COUNT = 3;
|
export const PREVIEW_AVATAR_COUNT = 3;
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,13 @@ import type {
|
|||||||
} from '../api/types';
|
} from '../api/types';
|
||||||
import type { MessageList, ThreadId } from '../types';
|
import type { MessageList, ThreadId } from '../types';
|
||||||
import type { ActionReturnType, GlobalState, SharedState } from './types';
|
import type { ActionReturnType, GlobalState, SharedState } from './types';
|
||||||
import { MAIN_THREAD_ID } from '../api/types';
|
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../api/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALL_FOLDER_ID, ANIMATION_LEVEL_DEFAULT,
|
ALL_FOLDER_ID, ANIMATION_LEVEL_DEFAULT,
|
||||||
ARCHIVED_FOLDER_ID,
|
ARCHIVED_FOLDER_ID,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
|
FOLDERS_POSITION_DEFAULT,
|
||||||
GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT,
|
GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT,
|
||||||
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
|
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
|
||||||
GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT,
|
GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT,
|
||||||
@ -21,7 +22,6 @@ import {
|
|||||||
IS_SCREEN_LOCKED_CACHE_KEY,
|
IS_SCREEN_LOCKED_CACHE_KEY,
|
||||||
SAVED_FOLDER_ID,
|
SAVED_FOLDER_ID,
|
||||||
SHARED_STATE_CACHE_KEY,
|
SHARED_STATE_CACHE_KEY,
|
||||||
TABS_POSITION_DEFAULT,
|
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { MAIN_IDB_STORE } from '../util/browser/idb';
|
import { MAIN_IDB_STORE } from '../util/browser/idb';
|
||||||
import { isUserId } from '../util/entities/ids';
|
import { isUserId } from '../util/entities/ids';
|
||||||
@ -314,7 +314,7 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
|||||||
cached.sharedState.settings = {
|
cached.sharedState.settings = {
|
||||||
canDisplayChatInTitle: untypedCached.settings.byKey.canDisplayChatInTitle,
|
canDisplayChatInTitle: untypedCached.settings.byKey.canDisplayChatInTitle,
|
||||||
animationLevel: untypedCached.settings.byKey.animationLevel,
|
animationLevel: untypedCached.settings.byKey.animationLevel,
|
||||||
tabsPosition: untypedCached.settings.byKey.tabsPosition,
|
foldersPosition: FOLDERS_POSITION_DEFAULT,
|
||||||
messageSendKeyCombo: untypedCached.settings.byKey.messageSendKeyCombo,
|
messageSendKeyCombo: untypedCached.settings.byKey.messageSendKeyCombo,
|
||||||
messageTextSize: untypedCached.settings.byKey.messageTextSize,
|
messageTextSize: untypedCached.settings.byKey.messageTextSize,
|
||||||
performance: untypedCached.settings.performance,
|
performance: untypedCached.settings.performance,
|
||||||
@ -350,8 +350,8 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
|||||||
cachedSharedSettings.performance = INITIAL_PERFORMANCE_STATE_MED;
|
cachedSharedSettings.performance = INITIAL_PERFORMANCE_STATE_MED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cachedSharedSettings.tabsPosition) {
|
if (!cachedSharedSettings.foldersPosition) {
|
||||||
cachedSharedSettings.tabsPosition = TABS_POSITION_DEFAULT;
|
cachedSharedSettings.foldersPosition = FOLDERS_POSITION_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cached.appConfig) {
|
if (!cached.appConfig) {
|
||||||
@ -473,7 +473,13 @@ export function serializeGlobal<T extends GlobalState>(global: T) {
|
|||||||
|
|
||||||
function reduceCustomEmojis<T extends GlobalState>(global: T): GlobalState['customEmojis'] {
|
function reduceCustomEmojis<T extends GlobalState>(global: T): GlobalState['customEmojis'] {
|
||||||
const { lastRendered, byId } = global.customEmojis;
|
const { lastRendered, byId } = global.customEmojis;
|
||||||
const idsToSave = lastRendered.slice(0, GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT);
|
const folderEmojiIds = Object.values(global.chatFolders.byId)
|
||||||
|
.flatMap((folder) => (
|
||||||
|
folder.title.entities
|
||||||
|
?.filter((entity) => entity.type === ApiMessageEntityTypes.CustomEmoji)
|
||||||
|
?.map((entity) => entity.documentId) || []
|
||||||
|
));
|
||||||
|
const idsToSave = unique([...folderEmojiIds, ...lastRendered]).slice(0, GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT);
|
||||||
const byIdToSave = pick(byId, idsToSave);
|
const byIdToSave = pick(byId, idsToSave);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -11,9 +11,9 @@ import {
|
|||||||
DEFAULT_PLAYBACK_RATE,
|
DEFAULT_PLAYBACK_RATE,
|
||||||
DEFAULT_RESALE_GIFTS_FILTER_OPTIONS,
|
DEFAULT_RESALE_GIFTS_FILTER_OPTIONS,
|
||||||
DEFAULT_VOLUME,
|
DEFAULT_VOLUME,
|
||||||
|
FOLDERS_POSITION_DEFAULT,
|
||||||
IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
||||||
MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX,
|
||||||
TABS_POSITION_DEFAULT,
|
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { IS_IOS, IS_MAC_OS } from '../util/browser/windowEnvironment';
|
import { IS_IOS, IS_MAC_OS } from '../util/browser/windowEnvironment';
|
||||||
import { DEFAULT_APP_CONFIG } from '../limits';
|
import { DEFAULT_APP_CONFIG } from '../limits';
|
||||||
@ -80,7 +80,7 @@ export const INITIAL_SHARED_STATE: SharedState = {
|
|||||||
? IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX
|
? IOS_DEFAULT_MESSAGE_TEXT_SIZE_PX
|
||||||
: (IS_MAC_OS ? MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX : DEFAULT_MESSAGE_TEXT_SIZE_PX),
|
: (IS_MAC_OS ? MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX : DEFAULT_MESSAGE_TEXT_SIZE_PX),
|
||||||
animationLevel: ANIMATION_LEVEL_DEFAULT,
|
animationLevel: ANIMATION_LEVEL_DEFAULT,
|
||||||
tabsPosition: TABS_POSITION_DEFAULT,
|
foldersPosition: FOLDERS_POSITION_DEFAULT,
|
||||||
messageSendKeyCombo: 'enter',
|
messageSendKeyCombo: 'enter',
|
||||||
performance: INITIAL_PERFORMANCE_STATE_MAX,
|
performance: INITIAL_PERFORMANCE_STATE_MAX,
|
||||||
shouldSkipWebAppCloseConfirmation: false,
|
shouldSkipWebAppCloseConfirmation: false,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import type { ApiLanguage } from '../../api/types';
|
import type { ApiLanguage } from '../../api/types';
|
||||||
import type { AnimationLevel, PerformanceType, Point, Size, TabsPosition, ThemeKey, TimeFormat } from '../../types';
|
import type {
|
||||||
|
AnimationLevel, FoldersPosition, PerformanceType, Point, Size, ThemeKey, TimeFormat,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
export interface SharedState {
|
export interface SharedState {
|
||||||
settings: SharedSettings;
|
settings: SharedSettings;
|
||||||
@ -14,7 +16,7 @@ export interface SharedSettings {
|
|||||||
performance: PerformanceType;
|
performance: PerformanceType;
|
||||||
messageTextSize: number;
|
messageTextSize: number;
|
||||||
animationLevel: AnimationLevel;
|
animationLevel: AnimationLevel;
|
||||||
tabsPosition: TabsPosition;
|
foldersPosition: FoldersPosition;
|
||||||
// This can be deleted after September 2025, along with the corresponding migration
|
// This can be deleted after September 2025, along with the corresponding migration
|
||||||
wasAnimationLevelSetManually?: boolean;
|
wasAnimationLevelSetManually?: boolean;
|
||||||
messageSendKeyCombo: 'enter' | 'ctrl-enter';
|
messageSendKeyCombo: 'enter' | 'ctrl-enter';
|
||||||
|
|||||||
@ -177,7 +177,7 @@ const useFolderTabs = ({
|
|||||||
title: sidebarMode ? lang('TabsPositionTop') : lang('TabsPositionLeft'),
|
title: sidebarMode ? lang('TabsPositionTop') : lang('TabsPositionLeft'),
|
||||||
icon: 'forums',
|
icon: 'forums',
|
||||||
handler: () => {
|
handler: () => {
|
||||||
setSharedSettingOption({ tabsPosition: sidebarMode ? 'top' : 'left' });
|
setSharedSettingOption({ foldersPosition: sidebarMode ? 'top' : 'left' });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -221,7 +221,7 @@ $color-message-story-mention-to: #74bcff;
|
|||||||
--border-radius-forum-avatar: 33.3333%;
|
--border-radius-forum-avatar: 33.3333%;
|
||||||
--messages-container-width: 45.5rem;
|
--messages-container-width: 45.5rem;
|
||||||
--right-column-width: 26.5rem;
|
--right-column-width: 26.5rem;
|
||||||
--tabs-sidebar-width: 5rem;
|
--folders-sidebar-width: 5rem;
|
||||||
--window-controls-width: 0rem;
|
--window-controls-width: 0rem;
|
||||||
--header-height: 3.5rem;
|
--header-height: 3.5rem;
|
||||||
--custom-emoji-size: 1.25rem;
|
--custom-emoji-size: 1.25rem;
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export type ThreadId = string | number;
|
|||||||
|
|
||||||
export type ThemeKey = 'light' | 'dark';
|
export type ThemeKey = 'light' | 'dark';
|
||||||
export type AnimationLevel = 0 | 1 | 2;
|
export type AnimationLevel = 0 | 1 | 2;
|
||||||
export type TabsPosition = 'top' | 'left';
|
export type FoldersPosition = 'top' | 'left';
|
||||||
export type PerformanceTypeKey = (
|
export type PerformanceTypeKey = (
|
||||||
'pageTransitions' | 'messageSendingAnimations' | 'mediaViewerAnimations'
|
'pageTransitions' | 'messageSendingAnimations' | 'mediaViewerAnimations'
|
||||||
| 'messageComposerAnimations' | 'contextMenuAnimations' | 'contextMenuBlur' | 'rightColumnAnimations'
|
| 'messageComposerAnimations' | 'contextMenuAnimations' | 'contextMenuBlur' | 'rightColumnAnimations'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user