Show story rings around avatars in send story modal
This commit is contained in:
parent
78e3120d1a
commit
d2322de4a3
8 changed files with 71 additions and 45 deletions
|
@ -8,6 +8,7 @@ import { SearchInput } from './SearchInput';
|
|||
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
||||
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { ConversationWithStoriesType } from '../state/selectors/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { PropsType as StoriesSettingsModalPropsType } from './StoriesSettingsModal';
|
||||
|
@ -46,7 +47,7 @@ export type PropsType = {
|
|||
distributionLists: Array<StoryDistributionListWithMembersDataType>;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
groupConversations: Array<ConversationType>;
|
||||
groupStories: Array<ConversationType>;
|
||||
groupStories: Array<ConversationWithStoriesType>;
|
||||
hasFirstStoryPostExperience: boolean;
|
||||
ourConversationId: string | undefined;
|
||||
i18n: LocalizerType;
|
||||
|
@ -712,6 +713,7 @@ export function SendStoryModal({
|
|||
isMe
|
||||
sharedGroupNames={me.sharedGroupNames}
|
||||
size={AvatarSize.THIRTY_TWO}
|
||||
storyRing={undefined}
|
||||
title={me.title}
|
||||
/>
|
||||
) : (
|
||||
|
@ -759,7 +761,7 @@ export function SendStoryModal({
|
|||
);
|
||||
};
|
||||
|
||||
const renderGroup = (group: ConversationType) => {
|
||||
const renderGroup = (group: ConversationWithStoriesType) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={selectedGroupIds.has(group.id)}
|
||||
|
@ -826,6 +828,7 @@ export function SendStoryModal({
|
|||
isMe={false}
|
||||
sharedGroupNames={[]}
|
||||
size={AvatarSize.THIRTY_TWO}
|
||||
storyRing={group.hasStories}
|
||||
title={group.title}
|
||||
/>
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import Measure from 'react-measure';
|
|||
import { noop } from 'lodash';
|
||||
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { ConversationWithStoriesType } from '../state/selectors/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { Row } from './ConversationList';
|
||||
|
@ -43,7 +44,7 @@ import { strictAssert } from '../util/assert';
|
|||
export type PropsType = {
|
||||
candidateConversations: Array<ConversationType>;
|
||||
distributionLists: Array<StoryDistributionListWithMembersDataType>;
|
||||
groupStories: Array<ConversationType>;
|
||||
groupStories: Array<ConversationWithStoriesType>;
|
||||
signalConnections: Array<ConversationType>;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
hideStoriesSettings: () => unknown;
|
||||
|
|
|
@ -58,6 +58,12 @@ import { isSignalConversation } from '../../util/isSignalConversation';
|
|||
import { reduce } from '../../util/iterables';
|
||||
import { getConversationTitleForPanelType } from '../../util/getConversationTitleForPanelType';
|
||||
import type { PanelRenderType } from '../../types/Panels';
|
||||
import type { HasStories } from '../../types/Stories';
|
||||
import { getHasStoriesSelector } from './stories2';
|
||||
|
||||
export type ConversationWithStoriesType = ConversationType & {
|
||||
hasStories?: HasStories;
|
||||
};
|
||||
|
||||
let placeholderContact: ConversationType;
|
||||
export const getPlaceholderContact = (): ConversationType => {
|
||||
|
@ -575,15 +581,22 @@ export const selectMostRecentActiveStoryTimestampByGroupOrDistributionList =
|
|||
export const getGroupStories = createSelector(
|
||||
getConversationLookup,
|
||||
getConversationIdsWithStories,
|
||||
getHasStoriesSelector,
|
||||
(
|
||||
conversationLookup: ConversationLookupType,
|
||||
conversationIdsWithStories: Set<string>
|
||||
): Array<ConversationType> => {
|
||||
return Object.values(conversationLookup).filter(
|
||||
conversation =>
|
||||
isGroupInStoryMode(conversation, conversationIdsWithStories) &&
|
||||
!conversation.left
|
||||
);
|
||||
conversationIdsWithStories: Set<string>,
|
||||
hasStoriesSelector
|
||||
): Array<ConversationWithStoriesType> => {
|
||||
return Object.values(conversationLookup)
|
||||
.filter(
|
||||
conversation =>
|
||||
isGroupInStoryMode(conversation, conversationIdsWithStories) &&
|
||||
!conversation.left
|
||||
)
|
||||
.map(conversation => ({
|
||||
...conversation,
|
||||
hasStories: hasStoriesSelector(conversation.id),
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -22,11 +22,7 @@ import type {
|
|||
StoriesStateType,
|
||||
AddStoryData,
|
||||
} from '../ducks/stories';
|
||||
import {
|
||||
HasStories,
|
||||
MY_STORY_ID,
|
||||
ResolvedSendStatus,
|
||||
} from '../../types/Stories';
|
||||
import { MY_STORY_ID, ResolvedSendStatus } from '../../types/Stories';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { SendStatus } from '../../messages/MessageSendState';
|
||||
import { canReply } from './message';
|
||||
|
@ -38,7 +34,6 @@ import {
|
|||
} from './conversations';
|
||||
import { getUserConversationId } from './user';
|
||||
import { getDistributionListSelector } from './storyDistributionLists';
|
||||
import { getStoriesEnabled } from './items';
|
||||
import { calculateExpirationTimestamp } from '../../util/expirationTimer';
|
||||
import { getMessageIdForLogging } from '../../util/idForLogging';
|
||||
import * as log from '../../logging/log';
|
||||
|
@ -499,32 +494,6 @@ export const getStoriesNotificationCount = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
export const getHasStoriesSelector = createSelector(
|
||||
getStoriesEnabled,
|
||||
getStoriesState,
|
||||
(isEnabled, { stories }) =>
|
||||
(conversationId?: string): HasStories | undefined => {
|
||||
if (!isEnabled || !conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conversationStories = stories.filter(
|
||||
story => story.conversationId === conversationId
|
||||
);
|
||||
|
||||
if (!conversationStories.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return conversationStories.some(
|
||||
story =>
|
||||
story.readStatus === ReadStatus.Unread && !story.deletedForEveryone
|
||||
)
|
||||
? HasStories.Unread
|
||||
: HasStories.Read;
|
||||
}
|
||||
);
|
||||
|
||||
export const getStoryByIdSelector = createSelector(
|
||||
getStoriesState,
|
||||
getUserConversationId,
|
||||
|
|
40
ts/state/selectors/stories2.ts
Normal file
40
ts/state/selectors/stories2.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { createSelector } from 'reselect';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { HasStories } from '../../types/Stories';
|
||||
import { getStoriesEnabled } from './items';
|
||||
|
||||
import type { StateType } from '../reducer';
|
||||
import type { StoriesStateType } from '../ducks/stories';
|
||||
|
||||
const getStoriesState = (state: StateType): StoriesStateType => state.stories;
|
||||
|
||||
// This exists solely to avoid circular import dependencies since it is required
|
||||
// by the conversations selector.
|
||||
export const getHasStoriesSelector = createSelector(
|
||||
getStoriesEnabled,
|
||||
getStoriesState,
|
||||
(isEnabled, { stories }) =>
|
||||
(conversationId?: string): HasStories | undefined => {
|
||||
if (!isEnabled || !conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conversationStories = stories.filter(
|
||||
story => story.conversationId === conversationId
|
||||
);
|
||||
|
||||
if (!conversationStories.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return conversationStories.some(
|
||||
story =>
|
||||
story.readStatus === ReadStatus.Unread && !story.deletedForEveryone
|
||||
)
|
||||
? HasStories.Unread
|
||||
: HasStories.Read;
|
||||
}
|
||||
);
|
|
@ -11,7 +11,7 @@ import { getAreWeASubscriber } from '../selectors/items';
|
|||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { getBadgesSelector } from '../selectors/badges';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
import { getHasStoriesSelector } from '../selectors/stories';
|
||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||
|
||||
const mapStateToProps = (state: StateType): PropsDataType => {
|
||||
const { contactId, conversationId } =
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
import { CallMode } from '../../types/Calling';
|
||||
import { getActiveCall, isAnybodyElseInGroupCall } from '../ducks/calling';
|
||||
import { getConversationCallMode } from '../ducks/conversations';
|
||||
import { getHasStoriesSelector } from '../selectors/stories';
|
||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
import { getUserACI, getIntl, getTheme } from '../selectors/user';
|
||||
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
||||
|
|
|
@ -9,7 +9,7 @@ import { ConversationHero } from '../../components/conversation/ConversationHero
|
|||
import type { StateType } from '../reducer';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { getHasStoriesSelector } from '../selectors/stories';
|
||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||
import { isSignalConversation } from '../../util/isSignalConversation';
|
||||
|
||||
type ExternalProps = {
|
||||
|
|
Loading…
Reference in a new issue