Auto-select a newly created distribution list or group when sending story

This commit is contained in:
Alvaro 2022-12-09 10:35:34 -07:00 committed by GitHub
parent 81e4564687
commit 2db14e8d6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 94 additions and 26 deletions

View file

@ -56,7 +56,7 @@ export type PropsType = {
onDistributionListCreated: (
name: string,
viewerUuids: Array<UUIDStringType>
) => unknown;
) => Promise<UUIDStringType>;
onSelectedStoryList: (options: {
conversationId: string;
distributionId: string | undefined;
@ -67,7 +67,7 @@ export type PropsType = {
conversationIds: Array<string>
) => unknown;
signalConnections: Array<ConversationType>;
toggleGroupsForStorySend: (cids: Array<string>) => unknown;
toggleGroupsForStorySend: (cids: Array<string>) => Promise<void>;
mostRecentActiveStoryTimestampByGroupOrDistributionList: Record<
string,
number
@ -419,9 +419,17 @@ export function SendStoryModal({
candidateConversations={candidateConversations}
getPreferredBadge={getPreferredBadge}
i18n={i18n}
onCreateList={(name, uuids) => {
onCreateList={async (name, uuids) => {
const newDistributionListId = await onDistributionListCreated(
name,
uuids
);
setSelectedContacts([]);
onDistributionListCreated(name, uuids);
setSelectedListIds(
listIds => new Set([...listIds, newDistributionListId])
);
setPage(Page.SendStory);
}}
onViewersUpdated={uuids => {
@ -480,9 +488,10 @@ export function SendStoryModal({
aria-label={i18n('ok')}
className="SendStoryModal__ok"
disabled={!chosenGroupIds.size}
onClick={() => {
toggleGroupsForStorySend(Array.from(chosenGroupIds));
onClick={async () => {
await toggleGroupsForStorySend(Array.from(chosenGroupIds));
setChosenGroupIds(new Set());
setSelectedGroupIds(chosenGroupIds);
setPage(Page.SendStory);
}}
type="button"

View file

@ -54,7 +54,7 @@ export type PropsType = {
onDistributionListCreated: (
name: string,
viewerUuids: Array<UUIDStringType>
) => unknown;
) => Promise<string>;
onHideMyStoriesFrom: (viewerUuids: Array<UUIDStringType>) => unknown;
onRemoveMembers: (listId: string, uuids: Array<UUIDStringType>) => unknown;
onRepliesNReactionsChanged: (

View file

@ -2,16 +2,45 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ActionCreatorsMapObject } from 'redux';
import type { ThunkAction } from 'redux-thunk';
import { bindActionCreators } from 'redux';
import { useDispatch } from 'react-redux';
import { useMemo } from 'react';
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Type-level function from an action creator (which may be ThunkAction creator) to a
* bound action creator.
*
* binding a thunk action creator changes it from:
* (params) => ThunkAction<R, ...>
* to:
* (params) => R
*
* a regular action creator's type is unchanged
*/
type BoundActionCreator<A> = A extends (
...params: infer P
) => ThunkAction<infer R, any, any, any>
? (...params: P) => R
: A;
export type BoundActionCreatorsMapObject<T extends ActionCreatorsMapObject> = {
[Property in keyof T]: BoundActionCreator<T[Property]>;
};
export const useBoundActions = <T extends ActionCreatorsMapObject>(
actions: T
): T => {
): BoundActionCreatorsMapObject<T> => {
const dispatch = useDispatch();
return useMemo(() => {
return bindActionCreators(actions, dispatch);
// bindActionCreators from redux has the wrong type when using thunk actions
// so we cast to the correct type
return bindActionCreators(
actions,
dispatch
) as any as BoundActionCreatorsMapObject<T>;
}, [actions, dispatch]);
};

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction } from 'redux-thunk';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import { Sound } from '../../util/Sound';
import * as Errors from '../../types/errors';
@ -106,7 +107,8 @@ export const actions = {
setIsPlaying,
};
export const useActions = (): typeof actions => useBoundActions(actions);
export const useActions = (): BoundActionCreatorsMapObject<typeof actions> =>
useBoundActions(actions);
function setCurrentTime(value: number): CurrentTimeUpdated {
globalMessageAudio.currentTime = value;

View file

@ -10,6 +10,7 @@ import type { StateType as RootStateType } from '../reducer';
import { fileToBytes } from '../../util/fileToBytes';
import { recorder } from '../../services/audioRecorder';
import { stringToMIMEType } from '../../types/MIME';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
export enum ErrorDialogAudioRecorderType {
@ -76,7 +77,8 @@ export const actions = {
startRecording,
};
export const useActions = (): typeof actions => useBoundActions(actions);
export const useActions = (): BoundActionCreatorsMapObject<typeof actions> =>
useBoundActions(actions);
function startRecording(): ThunkAction<
void,

View file

@ -83,6 +83,7 @@ import {
SelectedMessageSource,
} from './conversationsEnums';
import { markViewed as messageUpdaterMarkViewed } from '../../services/MessageUpdater';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import type { NoopActionType } from './noop';
@ -935,8 +936,9 @@ export const actions = {
verifyConversationsStoppingSend,
};
export const useConversationsActions = (): typeof actions =>
useBoundActions(actions);
export const useConversationsActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
function filterAvatarData(
avatars: ReadonlyArray<AvatarDataType>,
@ -2621,7 +2623,7 @@ function addMemberToGroup(
function toggleGroupsForStorySend(
conversationIds: Array<string>
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
): ThunkAction<Promise<void>, RootStateType, unknown, NoopActionType> {
return async dispatch => {
await Promise.all(
conversationIds.map(async conversationId => {

View file

@ -5,6 +5,7 @@ import { take, uniq } from 'lodash';
import type { ThunkAction } from 'redux-thunk';
import type { EmojiPickDataType } from '../../components/emoji/EmojiPicker';
import dataInterface from '../../sql/Client';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
const { updateEmojiUsage } = dataInterface;
@ -31,7 +32,8 @@ export const actions = {
useEmoji,
};
export const useActions = (): typeof actions => useBoundActions(actions);
export const useActions = (): BoundActionCreatorsMapObject<typeof actions> =>
useBoundActions(actions);
function onUseEmoji({
shortName,

View file

@ -12,6 +12,7 @@ import type { UUIDStringType } from '../../types/UUID';
import * as SingleServePromise from '../../services/singleServePromise';
import { getMessageById } from '../../messages/getMessageById';
import { getMessagePropsSelector } from '../selectors/message';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { longRunningTaskWrapper } from '../../util/longRunningTaskWrapper';
import { useBoundActions } from '../../hooks/useBoundActions';
import { isGroupV1 } from '../../util/whatTypeOfConversation';
@ -215,8 +216,9 @@ export const actions = {
closeGV2MigrationDialog,
};
export const useGlobalModalActions = (): typeof actions =>
useBoundActions(actions);
export const useGlobalModalActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
function hideContactModal(): HideContactModalActionType {
return {

View file

@ -6,6 +6,7 @@ import { v4 as getGuid } from 'uuid';
import type { ThunkAction } from 'redux-thunk';
import type { StateType as RootStateType } from '../reducer';
import * as storageShim from '../../shims/storage';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import type {
ConversationColorType,
@ -93,7 +94,8 @@ export const actions = {
resetItems,
};
export const useActions = (): typeof actions => useBoundActions(actions);
export const useActions = (): BoundActionCreatorsMapObject<typeof actions> =>
useBoundActions(actions);
function putItem<K extends keyof StorageAccessType>(
key: K,

View file

@ -12,6 +12,7 @@ import type {
} from '../../types/LinkPreview';
import { assignWithNoUnnecessaryAllocation } from '../../util/assignWithNoUnnecessaryAllocation';
import { maybeGrabLinkPreview } from '../../services/LinkPreview';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
// State
@ -84,8 +85,9 @@ export const actions = {
removeLinkPreview,
};
export const useLinkPreviewActions = (): typeof actions =>
useBoundActions(actions);
export const useLinkPreviewActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
// Reducer

View file

@ -6,6 +6,7 @@ import { omit } from 'lodash';
import * as log from '../../logging/log';
import * as Errors from '../../types/errors';
import { replaceIndex } from '../../util/replaceIndex';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import type { StateType as RootStateType } from '../reducer';
import { DEFAULT_PREFERRED_REACTION_EMOJI_SHORT_NAMES } from '../../reactions/constants';
@ -97,7 +98,8 @@ export const actions = {
selectDraftEmojiToBeReplaced,
};
export const useActions = (): typeof actions => useBoundActions(actions);
export const useActions = (): BoundActionCreatorsMapObject<typeof actions> =>
useBoundActions(actions);
function cancelCustomizePreferredReactionsModal(): CancelCustomizePreferredReactionsModalActionType {
return { type: CANCEL_CUSTOMIZE_PREFERRED_REACTIONS_MODAL };

View file

@ -51,6 +51,7 @@ import { isGroup } from '../../util/whatTypeOfConversation';
import { isNotNil } from '../../util/isNotNil';
import { isStory } from '../../messages/helpers';
import { sendStoryMessage as doSendStoryMessage } from '../../util/sendStoryMessage';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import { verifyStoryListMembers as doVerifyStoryListMembers } from '../../util/verifyStoryListMembers';
import { viewSyncJobQueue } from '../../jobs/viewSyncJobQueue';
@ -1317,7 +1318,9 @@ export const actions = {
setStorySending,
};
export const useStoriesActions = (): typeof actions => useBoundActions(actions);
export const useStoriesActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
// Reducer

View file

@ -14,6 +14,7 @@ import { UUID } from '../../types/UUID';
import { deleteStoryForEveryone } from '../../util/deleteStoryForEveryone';
import { replaceIndex } from '../../util/replaceIndex';
import { storageServiceUploadJob } from '../../services/storage';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
// State
@ -156,7 +157,12 @@ function createDistributionList(
memberUuids: Array<UUIDStringType>,
storageServiceDistributionListRecord?: StoryDistributionWithMembersType,
shouldSave = true
): ThunkAction<void, RootStateType, null, CreateListActionType> {
): ThunkAction<
Promise<UUIDStringType>,
RootStateType,
string,
CreateListActionType
> {
return async dispatch => {
const storyDistribution: StoryDistributionWithMembersType = {
allowsReplies: true,
@ -188,6 +194,8 @@ function createDistributionList(
name: storyDistribution.name,
},
});
return storyDistribution.id;
};
}
@ -483,8 +491,8 @@ export const actions = {
updateStoryViewers,
};
export const useStoryDistributionListsActions = (): typeof actions =>
useBoundActions(actions);
export const useStoryDistributionListsActions =
(): BoundActionCreatorsMapObject<typeof actions> => useBoundActions(actions);
// Reducer

View file

@ -1,6 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import type { ReplacementValuesType } from '../../types/Util';
@ -94,7 +95,9 @@ export const actions = {
showToast,
};
export const useToastActions = (): typeof actions => useBoundActions(actions);
export const useToastActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
// Reducer