Stories: Fix color for close friends (#4370)
This commit is contained in:
parent
1a98953d01
commit
6935d8d008
@ -239,6 +239,10 @@
|
|||||||
background-image: linear-gradient(215.87deg, var(--color-avatar-story-unread-from) -1.61%, var(--color-avatar-story-unread-to) 97.44%);
|
background-image: linear-gradient(215.87deg, var(--color-avatar-story-unread-from) -1.61%, var(--color-avatar-story-unread-to) 97.44%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.has-unread-story.close-friend::before {
|
||||||
|
background-image: linear-gradient(215.87deg, var(--color-avatar-story-friend-unread-from) -1.61%, var(--color-avatar-story-friend-unread-to) 97.44%);
|
||||||
|
}
|
||||||
|
|
||||||
.poster {
|
.poster {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@ -59,6 +59,7 @@ type OwnProps = {
|
|||||||
forPremiumPromo?: boolean;
|
forPremiumPromo?: boolean;
|
||||||
withStoryGap?: boolean;
|
withStoryGap?: boolean;
|
||||||
withStorySolid?: boolean;
|
withStorySolid?: boolean;
|
||||||
|
forceFriendStorySolid?: boolean;
|
||||||
forceUnreadStorySolid?: boolean;
|
forceUnreadStorySolid?: boolean;
|
||||||
storyViewerOrigin?: StoryViewerOrigin;
|
storyViewerOrigin?: StoryViewerOrigin;
|
||||||
storyViewerMode?: 'full' | 'single-peer' | 'disabled';
|
storyViewerMode?: 'full' | 'single-peer' | 'disabled';
|
||||||
@ -81,6 +82,7 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
forPremiumPromo,
|
forPremiumPromo,
|
||||||
withStoryGap,
|
withStoryGap,
|
||||||
withStorySolid,
|
withStorySolid,
|
||||||
|
forceFriendStorySolid,
|
||||||
forceUnreadStorySolid,
|
forceUnreadStorySolid,
|
||||||
storyViewerOrigin,
|
storyViewerOrigin,
|
||||||
storyViewerMode = 'single-peer',
|
storyViewerMode = 'single-peer',
|
||||||
@ -221,6 +223,7 @@ const Avatar: FC<OwnProps> = ({
|
|||||||
isRoundedRect && 'forum',
|
isRoundedRect && 'forum',
|
||||||
((withStory && peer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
((withStory && peer?.hasStories) || forPremiumPromo) && 'with-story-circle',
|
||||||
withStorySolid && peer?.hasStories && 'with-story-solid',
|
withStorySolid && peer?.hasStories && 'with-story-solid',
|
||||||
|
withStorySolid && forceFriendStorySolid && 'close-friend',
|
||||||
withStorySolid && (peer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',
|
withStorySolid && (peer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',
|
||||||
onClick && 'interactive',
|
onClick && 'interactive',
|
||||||
(!isSavedMessages && !imgBlobUrl) && 'no-photo',
|
(!isSavedMessages && !imgBlobUrl) && 'no-photo',
|
||||||
|
|||||||
@ -3,10 +3,11 @@ import React, {
|
|||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../global';
|
import { withGlobal } from '../../global';
|
||||||
|
|
||||||
|
import type { ApiTypeStory } from '../../api/types';
|
||||||
import type { ThemeKey } from '../../types';
|
import type { ThemeKey } from '../../types';
|
||||||
import type { AvatarSize } from './Avatar';
|
import type { AvatarSize } from './Avatar';
|
||||||
|
|
||||||
import { selectPeerStories, selectTheme, selectUser } from '../../global/selectors';
|
import { selectPeerStories, selectTheme } from '../../global/selectors';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { REM } from './helpers/mediaDimensions';
|
import { REM } from './helpers/mediaDimensions';
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ interface OwnProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
isCloseFriend?: boolean;
|
peerStories?: Record<number, ApiTypeStory>;
|
||||||
storyIds?: number[];
|
storyIds?: number[];
|
||||||
lastReadId?: number;
|
lastReadId?: number;
|
||||||
appTheme: ThemeKey;
|
appTheme: ThemeKey;
|
||||||
@ -58,7 +59,7 @@ const EXTRA_GAP_END = EXTRA_GAP_ANGLE + EXTRA_GAP_SIZE / 2;
|
|||||||
function AvatarStoryCircle({
|
function AvatarStoryCircle({
|
||||||
size = 'large',
|
size = 'large',
|
||||||
className,
|
className,
|
||||||
isCloseFriend,
|
peerStories,
|
||||||
storyIds,
|
storyIds,
|
||||||
lastReadId,
|
lastReadId,
|
||||||
withExtraGap,
|
withExtraGap,
|
||||||
@ -80,6 +81,21 @@ function AvatarStoryCircle({
|
|||||||
}, { total: 0, read: 0 });
|
}, { total: 0, read: 0 });
|
||||||
}, [lastReadId, storyIds]);
|
}, [lastReadId, storyIds]);
|
||||||
|
|
||||||
|
const isCloseFriend = useMemo(() => {
|
||||||
|
if (!peerStories || !storyIds?.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return storyIds.some((id) => {
|
||||||
|
const story = peerStories[id];
|
||||||
|
if (!story || !('isForCloseFriends' in story)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isRead = lastReadId && story.id <= lastReadId;
|
||||||
|
return story.isForCloseFriends && !isRead;
|
||||||
|
});
|
||||||
|
}, [lastReadId, peerStories, storyIds]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!ref.current) {
|
if (!ref.current) {
|
||||||
return;
|
return;
|
||||||
@ -113,12 +129,11 @@ function AvatarStoryCircle({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>((global, { peerId }): StateProps => {
|
export default memo(withGlobal<OwnProps>((global, { peerId }): StateProps => {
|
||||||
const user = selectUser(global, peerId);
|
|
||||||
const peerStories = selectPeerStories(global, peerId);
|
const peerStories = selectPeerStories(global, peerId);
|
||||||
const appTheme = selectTheme(global);
|
const appTheme = selectTheme(global);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isCloseFriend: user?.isCloseFriend,
|
peerStories: peerStories?.byId,
|
||||||
storyIds: peerStories?.orderedIds,
|
storyIds: peerStories?.orderedIds,
|
||||||
lastReadId: peerStories?.lastReadId,
|
lastReadId: peerStories?.lastReadId,
|
||||||
appTheme,
|
appTheme,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { memo, useEffect, useMemo } from '../../lib/teact/teact';
|
|||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { ApiChat, ApiUser } from '../../api/types';
|
import type { ApiChat, ApiUser } from '../../api/types';
|
||||||
|
import type { GlobalState } from '../../global/types';
|
||||||
|
|
||||||
import { ANIMATION_END_DELAY, PREVIEW_AVATAR_COUNT } from '../../config';
|
import { ANIMATION_END_DELAY, PREVIEW_AVATAR_COUNT } from '../../config';
|
||||||
import {
|
import {
|
||||||
@ -32,6 +33,7 @@ interface StateProps {
|
|||||||
withAnimation?: boolean;
|
withAnimation?: boolean;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
chatsById: Record<string, ApiChat>;
|
chatsById: Record<string, ApiChat>;
|
||||||
|
peerStories: GlobalState['stories']['byPeerId'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRELOAD_PEERS = 5;
|
const PRELOAD_PEERS = 5;
|
||||||
@ -46,6 +48,7 @@ function StoryToggler({
|
|||||||
isForumPanelOpen,
|
isForumPanelOpen,
|
||||||
isArchived,
|
isArchived,
|
||||||
withAnimation,
|
withAnimation,
|
||||||
|
peerStories,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const { toggleStoryRibbon } = getActions();
|
const { toggleStoryRibbon } = getActions();
|
||||||
|
|
||||||
@ -63,6 +66,24 @@ function StoryToggler({
|
|||||||
.reverse();
|
.reverse();
|
||||||
}, [currentUserId, orderedPeerIds, usersById, chatsById]);
|
}, [currentUserId, orderedPeerIds, usersById, chatsById]);
|
||||||
|
|
||||||
|
const closeFriends = useMemo(() => {
|
||||||
|
if (!peers?.length) return {};
|
||||||
|
return peers.reduce((acc, peer) => {
|
||||||
|
const stories = peerStories[peer.id];
|
||||||
|
if (!stories) return acc;
|
||||||
|
|
||||||
|
const isCloseFriend = stories.orderedIds.some((id) => {
|
||||||
|
const story = stories.byId[id];
|
||||||
|
if (!story || !('isForCloseFriends' in story)) return false;
|
||||||
|
const isRead = stories.lastReadId && story.id <= stories.lastReadId;
|
||||||
|
return story.isForCloseFriends && !isRead;
|
||||||
|
});
|
||||||
|
|
||||||
|
acc[peer.id] = isCloseFriend;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, boolean>);
|
||||||
|
}, [peerStories, peers]);
|
||||||
|
|
||||||
const preloadPeerIds = useMemo(() => {
|
const preloadPeerIds = useMemo(() => {
|
||||||
return orderedPeerIds.slice(0, PRELOAD_PEERS);
|
return orderedPeerIds.slice(0, PRELOAD_PEERS);
|
||||||
}, [orderedPeerIds]);
|
}, [orderedPeerIds]);
|
||||||
@ -104,6 +125,7 @@ function StoryToggler({
|
|||||||
size="tiny"
|
size="tiny"
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
withStorySolid
|
withStorySolid
|
||||||
|
forceFriendStorySolid={closeFriends[peer.id]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</button>
|
</button>
|
||||||
@ -111,7 +133,7 @@ function StoryToggler({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>((global, { isArchived }): StateProps => {
|
export default memo(withGlobal<OwnProps>((global, { isArchived }): StateProps => {
|
||||||
const { orderedPeerIds: { archived, active } } = global.stories;
|
const { orderedPeerIds: { archived, active }, byPeerId } = global.stories;
|
||||||
const { storyViewer: { isRibbonShown, isArchivedRibbonShown } } = selectTabState(global);
|
const { storyViewer: { isRibbonShown, isArchivedRibbonShown } } = selectTabState(global);
|
||||||
const isForumPanelOpen = selectIsForumPanelOpen(global);
|
const isForumPanelOpen = selectIsForumPanelOpen(global);
|
||||||
const withAnimation = selectPerformanceSettingsValue(global, 'storyRibbonAnimations');
|
const withAnimation = selectPerformanceSettingsValue(global, 'storyRibbonAnimations');
|
||||||
@ -124,5 +146,6 @@ export default memo(withGlobal<OwnProps>((global, { isArchived }): StateProps =>
|
|||||||
withAnimation,
|
withAnimation,
|
||||||
usersById: global.users.byId,
|
usersById: global.users.byId,
|
||||||
chatsById: global.chats.byId,
|
chatsById: global.chats.byId,
|
||||||
|
peerStories: byPeerId,
|
||||||
};
|
};
|
||||||
})(StoryToggler));
|
})(StoryToggler));
|
||||||
|
|||||||
@ -162,8 +162,10 @@ $color-message-story-mention-to: #74bcff;
|
|||||||
--color-selection-highlight: #{$color-selection};
|
--color-selection-highlight: #{$color-selection};
|
||||||
--color-selection-highlight-emoji: rgba(#{toRGB($color-selection)}, 0.7);
|
--color-selection-highlight-emoji: rgba(#{toRGB($color-selection)}, 0.7);
|
||||||
|
|
||||||
--color-avatar-story-unread-from: #34c76f;
|
--color-avatar-story-unread-from: #34c578;
|
||||||
--color-avatar-story-unread-to: #3da1fd;
|
--color-avatar-story-unread-to: #3ca3f3;
|
||||||
|
--color-avatar-story-friend-unread-from: #c9eb38;
|
||||||
|
--color-avatar-story-friend-unread-to: #09c167;
|
||||||
|
|
||||||
--color-default-shadow: #72727240;
|
--color-default-shadow: #72727240;
|
||||||
--color-light-shadow: #7272722b;
|
--color-light-shadow: #7272722b;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user