Show story rings around avatars in send story modal

This commit is contained in:
Josh Perez 2023-03-07 21:15:25 -05:00 committed by GitHub
parent 78e3120d1a
commit d2322de4a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 45 deletions

View file

@ -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}
/>

View file

@ -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;

View file

@ -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),
}));
}
);

View file

@ -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,

View 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;
}
);

View file

@ -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 } =

View file

@ -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';

View file

@ -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 = {