Show and sort active groups when sending a story
This commit is contained in:
parent
5078bc83f4
commit
d0fb25f758
9 changed files with 336 additions and 228 deletions
|
@ -5779,6 +5779,22 @@
|
||||||
"message": "Only admins can send stories to this group.",
|
"message": "Only admins can send stories to this group.",
|
||||||
"description": "Alert body for groups that non-admins cannot send stories to"
|
"description": "Alert body for groups that non-admins cannot send stories to"
|
||||||
},
|
},
|
||||||
|
"icu:SendStoryModal__my-stories-description-all": {
|
||||||
|
"messageformat": "All Signal connections · {viewersCount, plural, one {1 viewer} other {# viewers}}",
|
||||||
|
"description": "Shown as a subtitle under My Stories option in the send-story-to dialog when not exluding anyone"
|
||||||
|
},
|
||||||
|
"icu:SendStoryModal__my-stories-description-excluding": {
|
||||||
|
"messageformat": "All Signal connections · {excludedCount, plural, one {1 excluded} other {# excluded}}",
|
||||||
|
"description": "Shown as a subtitle under My Stories option in the send-story-to dialog when excluding some"
|
||||||
|
},
|
||||||
|
"icu:SendStoryModal__private-story-description": {
|
||||||
|
"messageformat": "Private story · {viewersCount, plural, one {1 viewer} other {# viewers}}",
|
||||||
|
"description": "Shown as a subtitle of each private story in the send-story-to dialog"
|
||||||
|
},
|
||||||
|
"icu:SendStoryModal__group-story-description": {
|
||||||
|
"messageformat": "Group story · {membersCount, plural, one {1 member} other {# members}}",
|
||||||
|
"description": "Shown as a subtitle of each group story in the send-story-to dialog"
|
||||||
|
},
|
||||||
"Stories__settings-toggle--title": {
|
"Stories__settings-toggle--title": {
|
||||||
"message": "Share & View Stories",
|
"message": "Share & View Stories",
|
||||||
"description": "Select box title for the stories on/off toggle"
|
"description": "Select box title for the stories on/off toggle"
|
||||||
|
|
|
@ -136,6 +136,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__distribution-list {
|
&__distribution-list {
|
||||||
|
height: 52px;
|
||||||
&__container {
|
&__container {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { noop } from 'lodash';
|
import { noop, sortBy } from 'lodash';
|
||||||
|
|
||||||
import { SearchInput } from './SearchInput';
|
import { SearchInput } from './SearchInput';
|
||||||
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
||||||
|
@ -62,6 +62,10 @@ export type PropsType = {
|
||||||
) => unknown;
|
) => unknown;
|
||||||
signalConnections: Array<ConversationType>;
|
signalConnections: Array<ConversationType>;
|
||||||
toggleGroupsForStorySend: (cids: Array<string>) => unknown;
|
toggleGroupsForStorySend: (cids: Array<string>) => unknown;
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList: Record<
|
||||||
|
string,
|
||||||
|
number
|
||||||
|
>;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
StoriesSettingsModalPropsType,
|
StoriesSettingsModalPropsType,
|
||||||
| 'onHideMyStoriesFrom'
|
| 'onHideMyStoriesFrom'
|
||||||
|
@ -137,6 +141,7 @@ export const SendStoryModal = ({
|
||||||
setMyStoriesToAllSignalConnections,
|
setMyStoriesToAllSignalConnections,
|
||||||
signalConnections,
|
signalConnections,
|
||||||
toggleGroupsForStorySend,
|
toggleGroupsForStorySend,
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList,
|
||||||
toggleSignalConnectionsModal,
|
toggleSignalConnectionsModal,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
const [page, setPage] = useState<PageType>(Page.SendStory);
|
const [page, setPage] = useState<PageType>(Page.SendStory);
|
||||||
|
@ -240,7 +245,7 @@ export const SendStoryModal = ({
|
||||||
[distributionLists]
|
[distributionLists]
|
||||||
);
|
);
|
||||||
|
|
||||||
const initialMyStories = useMemo(
|
const initialMyStories: StoryDistributionListWithMembersDataType = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
allowsReplies: true,
|
allowsReplies: true,
|
||||||
id: MY_STORIES_ID,
|
id: MY_STORIES_ID,
|
||||||
|
@ -597,73 +602,25 @@ export const SendStoryModal = ({
|
||||||
url: objectUrl,
|
url: objectUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
modal = handleClose => (
|
// my stories always first, the rest sorted by recency
|
||||||
<ModalPage
|
const fullList = sortBy(
|
||||||
modalName="SendStoryModal__title"
|
[...groupStories, ...distributionLists],
|
||||||
title={i18n('SendStoryModal__title')}
|
listOrGroup => {
|
||||||
moduleClassName="SendStoryModal"
|
if (listOrGroup.id === MY_STORIES_ID) {
|
||||||
modalFooter={footer}
|
return Number.NEGATIVE_INFINITY;
|
||||||
onClose={handleClose}
|
}
|
||||||
{...modalCommonProps}
|
return (
|
||||||
>
|
(mostRecentActiveStoryTimestampByGroupOrDistributionList[
|
||||||
<div
|
listOrGroup.id
|
||||||
className="SendStoryModal__story-preview"
|
] ?? 0) * -1
|
||||||
style={{ backgroundImage: getStoryBackground(attachment) }}
|
);
|
||||||
>
|
}
|
||||||
<StoryImage
|
);
|
||||||
i18n={i18n}
|
|
||||||
firstName={i18n('you')}
|
const renderDistributionList = (
|
||||||
queueStoryDownload={noop}
|
list: StoryDistributionListWithMembersDataType
|
||||||
storyId="story-id"
|
): JSX.Element => {
|
||||||
label="label"
|
return (
|
||||||
moduleClassName="SendStoryModal__story"
|
|
||||||
attachment={attachment}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="SendStoryModal__top-bar">
|
|
||||||
{i18n('stories')}
|
|
||||||
<ContextMenu
|
|
||||||
aria-label={i18n('SendStoryModal__new')}
|
|
||||||
i18n={i18n}
|
|
||||||
menuOptions={[
|
|
||||||
{
|
|
||||||
label: i18n('SendStoryModal__new-custom--title'),
|
|
||||||
description: i18n('SendStoryModal__new-custom--description'),
|
|
||||||
icon: 'SendStoryModal__icon--custom',
|
|
||||||
onClick: () => setPage(Page.ChooseViewers),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n('SendStoryModal__new-group--title'),
|
|
||||||
description: i18n('SendStoryModal__new-group--description'),
|
|
||||||
icon: 'SendStoryModal__icon--group',
|
|
||||||
onClick: () => setPage(Page.ChooseGroups),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
moduleClassName="SendStoryModal__new-story"
|
|
||||||
popperOptions={{
|
|
||||||
placement: 'bottom',
|
|
||||||
strategy: 'absolute',
|
|
||||||
}}
|
|
||||||
theme={Theme.Dark}
|
|
||||||
>
|
|
||||||
{({ openMenu, onKeyDown, ref, menuNode }) => (
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
ref={ref}
|
|
||||||
className="SendStoryModal__new-story__button"
|
|
||||||
variant={ButtonVariant.Secondary}
|
|
||||||
size={ButtonSize.Small}
|
|
||||||
onClick={openMenu}
|
|
||||||
onKeyDown={onKeyDown}
|
|
||||||
>
|
|
||||||
{i18n('SendStoryModal__new')}
|
|
||||||
</Button>
|
|
||||||
{menuNode}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ContextMenu>
|
|
||||||
</div>
|
|
||||||
{distributionLists.map(list => (
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedListIds.has(list.id)}
|
checked={selectedListIds.has(list.id)}
|
||||||
key={list.id}
|
key={list.id}
|
||||||
|
@ -689,9 +646,7 @@ export const SendStoryModal = ({
|
||||||
return new Set([...listIds]);
|
return new Set([...listIds]);
|
||||||
});
|
});
|
||||||
if (value) {
|
if (value) {
|
||||||
onSelectedStoryList(
|
onSelectedStoryList(getListMemberUuids(list, signalConnections));
|
||||||
getListMemberUuids(list, signalConnections)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -788,8 +743,11 @@ export const SendStoryModal = ({
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
)}
|
)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
))}
|
);
|
||||||
{groupStories.map(group => (
|
};
|
||||||
|
|
||||||
|
const renderGroup = (group: ConversationType) => {
|
||||||
|
return (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedGroupIds.has(group.id)}
|
checked={selectedGroupIds.has(group.id)}
|
||||||
key={group.id}
|
key={group.id}
|
||||||
|
@ -878,7 +836,81 @@ export const SendStoryModal = ({
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
)}
|
)}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
))}
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
modal = handleClose => (
|
||||||
|
<ModalPage
|
||||||
|
modalName="SendStoryModal__title"
|
||||||
|
title={i18n('SendStoryModal__title')}
|
||||||
|
moduleClassName="SendStoryModal"
|
||||||
|
modalFooter={footer}
|
||||||
|
onClose={handleClose}
|
||||||
|
{...modalCommonProps}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="SendStoryModal__story-preview"
|
||||||
|
style={{ backgroundImage: getStoryBackground(attachment) }}
|
||||||
|
>
|
||||||
|
<StoryImage
|
||||||
|
i18n={i18n}
|
||||||
|
firstName={i18n('you')}
|
||||||
|
queueStoryDownload={noop}
|
||||||
|
storyId="story-id"
|
||||||
|
label="label"
|
||||||
|
moduleClassName="SendStoryModal__story"
|
||||||
|
attachment={attachment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="SendStoryModal__top-bar">
|
||||||
|
{i18n('stories')}
|
||||||
|
<ContextMenu
|
||||||
|
aria-label={i18n('SendStoryModal__new')}
|
||||||
|
i18n={i18n}
|
||||||
|
menuOptions={[
|
||||||
|
{
|
||||||
|
label: i18n('SendStoryModal__new-custom--title'),
|
||||||
|
description: i18n('SendStoryModal__new-custom--description'),
|
||||||
|
icon: 'SendStoryModal__icon--custom',
|
||||||
|
onClick: () => setPage(Page.ChooseViewers),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n('SendStoryModal__new-group--title'),
|
||||||
|
description: i18n('SendStoryModal__new-group--description'),
|
||||||
|
icon: 'SendStoryModal__icon--group',
|
||||||
|
onClick: () => setPage(Page.ChooseGroups),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
moduleClassName="SendStoryModal__new-story"
|
||||||
|
popperOptions={{
|
||||||
|
placement: 'bottom',
|
||||||
|
strategy: 'absolute',
|
||||||
|
}}
|
||||||
|
theme={Theme.Dark}
|
||||||
|
>
|
||||||
|
{({ openMenu, onKeyDown, ref, menuNode }) => (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
className="SendStoryModal__new-story__button"
|
||||||
|
variant={ButtonVariant.Secondary}
|
||||||
|
size={ButtonSize.Small}
|
||||||
|
onClick={openMenu}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
>
|
||||||
|
{i18n('SendStoryModal__new')}
|
||||||
|
</Button>
|
||||||
|
{menuNode}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ContextMenu>
|
||||||
|
</div>
|
||||||
|
{fullList.map(listOrGroup =>
|
||||||
|
// only group has a type field
|
||||||
|
'type' in listOrGroup
|
||||||
|
? renderGroup(listOrGroup)
|
||||||
|
: renderDistributionList(listOrGroup)
|
||||||
|
)}
|
||||||
</ModalPage>
|
</ModalPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ export type PropsType = {
|
||||||
| 'setMyStoriesToAllSignalConnections'
|
| 'setMyStoriesToAllSignalConnections'
|
||||||
| 'signalConnections'
|
| 'signalConnections'
|
||||||
| 'toggleGroupsForStorySend'
|
| 'toggleGroupsForStorySend'
|
||||||
|
| 'mostRecentActiveStoryTimestampByGroupOrDistributionList'
|
||||||
| 'toggleSignalConnectionsModal'
|
| 'toggleSignalConnectionsModal'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ export const StoryCreator = ({
|
||||||
setMyStoriesToAllSignalConnections,
|
setMyStoriesToAllSignalConnections,
|
||||||
signalConnections,
|
signalConnections,
|
||||||
toggleGroupsForStorySend,
|
toggleGroupsForStorySend,
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList,
|
||||||
toggleSignalConnectionsModal,
|
toggleSignalConnectionsModal,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
const [draftAttachment, setDraftAttachment] = useState<
|
const [draftAttachment, setDraftAttachment] = useState<
|
||||||
|
@ -171,6 +173,9 @@ export const StoryCreator = ({
|
||||||
}
|
}
|
||||||
signalConnections={signalConnections}
|
signalConnections={signalConnections}
|
||||||
toggleGroupsForStorySend={toggleGroupsForStorySend}
|
toggleGroupsForStorySend={toggleGroupsForStorySend}
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList={
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList
|
||||||
|
}
|
||||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -5526,6 +5526,7 @@ export class ConversationModel extends window.Backbone
|
||||||
return window.textsecure.storage.protocol.signAlternateIdentity();
|
return window.textsecure.storage.protocol.signAlternateIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return only undefined if not a group */
|
||||||
getStorySendMode(): StorySendMode | undefined {
|
getStorySendMode(): StorySendMode | undefined {
|
||||||
if (!isGroup(this.attributes)) {
|
if (!isGroup(this.attributes)) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -916,7 +916,7 @@ async function getAvatarsAndUpdateConversation(
|
||||||
conversation.attributes.avatars = nextAvatars.map(avatarData =>
|
conversation.attributes.avatars = nextAvatars.map(avatarData =>
|
||||||
omit(avatarData, ['buffer'])
|
omit(avatarData, ['buffer'])
|
||||||
);
|
);
|
||||||
await window.Signal.Data.updateConversation(conversation.attributes);
|
window.Signal.Data.updateConversation(conversation.attributes);
|
||||||
|
|
||||||
return nextAvatars;
|
return nextAvatars;
|
||||||
}
|
}
|
||||||
|
@ -1264,7 +1264,7 @@ export function setVoiceNotePlaybackRate({
|
||||||
} else {
|
} else {
|
||||||
conversationModel.attributes.voiceNotePlaybackRate = rate;
|
conversationModel.attributes.voiceNotePlaybackRate = rate;
|
||||||
}
|
}
|
||||||
await window.Signal.Data.updateConversation(conversationModel.attributes);
|
window.Signal.Data.updateConversation(conversationModel.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversation = conversationModel?.format();
|
const conversation = conversationModel?.format();
|
||||||
|
@ -1314,7 +1314,7 @@ function colorSelected({
|
||||||
delete conversation.attributes.customColorId;
|
delete conversation.attributes.customColorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
await window.Signal.Data.updateConversation(conversation.attributes);
|
window.Signal.Data.updateConversation(conversation.attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -2016,7 +2016,7 @@ function toggleGroupsForStorySend(
|
||||||
conversation.set({
|
conversation.set({
|
||||||
storySendMode: newStorySendMode,
|
storySendMode: newStorySendMode,
|
||||||
});
|
});
|
||||||
await window.Signal.Data.updateConversation(conversation.attributes);
|
window.Signal.Data.updateConversation(conversation.attributes);
|
||||||
conversation.captureChange('storySendMode');
|
conversation.captureChange('storySendMode');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import type {
|
||||||
MessagesByConversationType,
|
MessagesByConversationType,
|
||||||
PreJoinConversationType,
|
PreJoinConversationType,
|
||||||
} from '../ducks/conversations';
|
} from '../ducks/conversations';
|
||||||
import type { StoriesStateType } from '../ducks/stories';
|
import type { StoriesStateType, StoryDataType } from '../ducks/stories';
|
||||||
import {
|
import {
|
||||||
ComposerStep,
|
ComposerStep,
|
||||||
OneTimeModalState,
|
OneTimeModalState,
|
||||||
|
@ -61,6 +61,7 @@ import type { AccountSelectorType } from './accounts';
|
||||||
import { getAccountSelector } from './accounts';
|
import { getAccountSelector } from './accounts';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { TimelineMessageLoadingState } from '../../util/timelineUtil';
|
import { TimelineMessageLoadingState } from '../../util/timelineUtil';
|
||||||
|
import { reduce } from '../../util/iterables';
|
||||||
|
|
||||||
let placeholderContact: ConversationType;
|
let placeholderContact: ConversationType;
|
||||||
export const getPlaceholderContact = (): ConversationType => {
|
export const getPlaceholderContact = (): ConversationType => {
|
||||||
|
@ -545,6 +546,29 @@ export const getNonGroupStories = createSelector(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectMostRecentActiveStoryTimestampByGroupOrDistributionList =
|
||||||
|
createSelector(
|
||||||
|
(state: StateType): Array<StoryDataType> => state.stories.stories,
|
||||||
|
(stories: Array<StoryDataType>): Record<string, number> => {
|
||||||
|
return reduce<StoryDataType, Record<string, number>>(
|
||||||
|
stories,
|
||||||
|
(acc, story) => {
|
||||||
|
const distributionListOrConversationId =
|
||||||
|
story.storyDistributionListId ?? story.conversationId;
|
||||||
|
const cur = acc[distributionListOrConversationId];
|
||||||
|
if (cur && story.timestamp < cur) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[distributionListOrConversationId]: story.timestamp,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const getGroupStories = createSelector(
|
export const getGroupStories = createSelector(
|
||||||
getConversationLookup,
|
getConversationLookup,
|
||||||
getConversationIdsWithStories,
|
getConversationIdsWithStories,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
getGroupStories,
|
getGroupStories,
|
||||||
getMe,
|
getMe,
|
||||||
getNonGroupStories,
|
getNonGroupStories,
|
||||||
|
selectMostRecentActiveStoryTimestampByGroupOrDistributionList,
|
||||||
} from '../selectors/conversations';
|
} from '../selectors/conversations';
|
||||||
import { getDistributionListsWithMembers } from '../selectors/storyDistributionLists';
|
import { getDistributionListsWithMembers } from '../selectors/storyDistributionLists';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
|
@ -70,6 +71,9 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
const me = useSelector(getMe);
|
const me = useSelector(getMe);
|
||||||
const recentStickers = useSelector(getRecentStickers);
|
const recentStickers = useSelector(getRecentStickers);
|
||||||
const signalConnections = useSelector(getAllSignalConnections);
|
const signalConnections = useSelector(getAllSignalConnections);
|
||||||
|
const mostRecentActiveStoryTimestampByGroupOrDistributionList = useSelector(
|
||||||
|
selectMostRecentActiveStoryTimestampByGroupOrDistributionList
|
||||||
|
);
|
||||||
|
|
||||||
const addStoryData = useSelector(getAddStoryData);
|
const addStoryData = useSelector(getAddStoryData);
|
||||||
const file = addStoryData?.type === 'Media' ? addStoryData.file : undefined;
|
const file = addStoryData?.type === 'Media' ? addStoryData.file : undefined;
|
||||||
|
@ -106,6 +110,9 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
||||||
signalConnections={signalConnections}
|
signalConnections={signalConnections}
|
||||||
toggleGroupsForStorySend={toggleGroupsForStorySend}
|
toggleGroupsForStorySend={toggleGroupsForStorySend}
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList={
|
||||||
|
mostRecentActiveStoryTimestampByGroupOrDistributionList
|
||||||
|
}
|
||||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type { UUIDStringType } from '../types/UUID';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import dataInterface from '../sql/Client';
|
import dataInterface from '../sql/Client';
|
||||||
import { DAY, SECOND } from './durations';
|
import { DAY, SECOND } from './durations';
|
||||||
import { MY_STORIES_ID } from '../types/Stories';
|
import { MY_STORIES_ID, StorySendMode } from '../types/Stories';
|
||||||
import { getStoriesBlocked } from './stories';
|
import { getStoriesBlocked } from './stories';
|
||||||
import { ReadStatus } from '../messages/MessageReadStatus';
|
import { ReadStatus } from '../messages/MessageReadStatus';
|
||||||
import { SeenStatus } from '../MessageSeenStatus';
|
import { SeenStatus } from '../MessageSeenStatus';
|
||||||
|
@ -23,6 +23,7 @@ import { getSignalConnections } from './getSignalConnections';
|
||||||
import { incrementMessageCounter } from './incrementMessageCounter';
|
import { incrementMessageCounter } from './incrementMessageCounter';
|
||||||
import { isGroupV2 } from './whatTypeOfConversation';
|
import { isGroupV2 } from './whatTypeOfConversation';
|
||||||
import { isNotNil } from './isNotNil';
|
import { isNotNil } from './isNotNil';
|
||||||
|
import { collect } from './iterables';
|
||||||
|
|
||||||
export async function sendStoryMessage(
|
export async function sendStoryMessage(
|
||||||
listIds: Array<string>,
|
listIds: Array<string>,
|
||||||
|
@ -180,8 +181,8 @@ export async function sendStoryMessage(
|
||||||
MessageAttributesType
|
MessageAttributesType
|
||||||
>();
|
>();
|
||||||
|
|
||||||
await Promise.all(
|
const groupsToSendTo = Array.from(
|
||||||
conversationIds.map(async (conversationId, index) => {
|
collect(conversationIds, conversationId => {
|
||||||
const group = window.ConversationController.get(conversationId);
|
const group = window.ConversationController.get(conversationId);
|
||||||
|
|
||||||
if (!group) {
|
if (!group) {
|
||||||
|
@ -208,6 +209,27 @@ export async function sendStoryMessage(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// sending a story to a group marks it as one we want to always
|
||||||
|
// include on the send-story-to list
|
||||||
|
const groupsToUpdate = Array.from(groupsToSendTo).filter(
|
||||||
|
group => group.getStorySendMode() !== StorySendMode.Always
|
||||||
|
);
|
||||||
|
for (const group of groupsToUpdate) {
|
||||||
|
group.set('storySendMode', StorySendMode.Always);
|
||||||
|
}
|
||||||
|
window.Signal.Data.updateConversations(
|
||||||
|
groupsToUpdate.map(group => group.attributes)
|
||||||
|
);
|
||||||
|
for (const group of groupsToUpdate) {
|
||||||
|
group.captureChange('storySendMode');
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
groupsToSendTo.map(async (group, index) => {
|
||||||
// We want all of these timestamps to be different from the My Story timestamp.
|
// We want all of these timestamps to be different from the My Story timestamp.
|
||||||
const groupTimestamp = timestamp + index + 1;
|
const groupTimestamp = timestamp + index + 1;
|
||||||
|
|
||||||
|
@ -238,7 +260,7 @@ export async function sendStoryMessage(
|
||||||
await window.Signal.Migrations.upgradeMessageSchema({
|
await window.Signal.Migrations.upgradeMessageSchema({
|
||||||
attachments,
|
attachments,
|
||||||
canReplyToStory: true,
|
canReplyToStory: true,
|
||||||
conversationId,
|
conversationId: group.id,
|
||||||
expireTimer: DAY / SECOND,
|
expireTimer: DAY / SECOND,
|
||||||
expirationStartTimestamp: Date.now(),
|
expirationStartTimestamp: Date.now(),
|
||||||
id: UUID.generate().toString(),
|
id: UUID.generate().toString(),
|
||||||
|
@ -255,7 +277,7 @@ export async function sendStoryMessage(
|
||||||
type: 'story',
|
type: 'story',
|
||||||
});
|
});
|
||||||
|
|
||||||
groupV2MessagesByConversationId.set(conversationId, messageAttributes);
|
groupV2MessagesByConversationId.set(group.id, messageAttributes);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue