Notification for failed story sends
This commit is contained in:
parent
4c2f169783
commit
e11f961d7a
7 changed files with 67 additions and 3 deletions
|
@ -6315,6 +6315,14 @@
|
||||||
"message": "Add a link for viewers of your story",
|
"message": "Add a link for viewers of your story",
|
||||||
"description": "Empty state for the link preview"
|
"description": "Empty state for the link preview"
|
||||||
},
|
},
|
||||||
|
"icu:Stories__failed-send--full": {
|
||||||
|
"messageformat": "Story failed to send",
|
||||||
|
"description": "Notification text whenever a story fails to send"
|
||||||
|
},
|
||||||
|
"icu:Stories__failed-send--partial": {
|
||||||
|
"messageformat": "Story couldn't be sent to all recipients",
|
||||||
|
"description": "Notification text whenever a story partially fails to send"
|
||||||
|
},
|
||||||
"TextAttachment__placeholder": {
|
"TextAttachment__placeholder": {
|
||||||
"message": "Add text",
|
"message": "Add text",
|
||||||
"description": "Placeholder for the add text input"
|
"description": "Placeholder for the add text input"
|
||||||
|
|
|
@ -28,6 +28,7 @@ export type PropsType = {
|
||||||
profileName?: string;
|
profileName?: string;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
title: string;
|
title: string;
|
||||||
|
hasFailedStorySends?: boolean;
|
||||||
unreadStoriesCount: number;
|
unreadStoriesCount: number;
|
||||||
|
|
||||||
showArchivedConversations: () => void;
|
showArchivedConversations: () => void;
|
||||||
|
@ -149,6 +150,7 @@ export class MainHeader extends React.Component<PropsType, StateType> {
|
||||||
avatarPath,
|
avatarPath,
|
||||||
badge,
|
badge,
|
||||||
color,
|
color,
|
||||||
|
hasFailedStorySends,
|
||||||
hasPendingUpdate,
|
hasPendingUpdate,
|
||||||
i18n,
|
i18n,
|
||||||
name,
|
name,
|
||||||
|
@ -251,7 +253,10 @@ export class MainHeader extends React.Component<PropsType, StateType> {
|
||||||
title={i18n('stories')}
|
title={i18n('stories')}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{unreadStoriesCount ? (
|
{hasFailedStorySends && (
|
||||||
|
<span className="module-main-header__stories-badge">!</span>
|
||||||
|
)}
|
||||||
|
{!hasFailedStorySends && unreadStoriesCount ? (
|
||||||
<span className="module-main-header__stories-badge">
|
<span className="module-main-header__stories-badge">
|
||||||
{unreadStoriesCount}
|
{unreadStoriesCount}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -440,6 +440,8 @@ export async function sendStory(
|
||||||
const oldSendStateByConversationId =
|
const oldSendStateByConversationId =
|
||||||
message.get('sendStateByConversationId') || {};
|
message.get('sendStateByConversationId') || {};
|
||||||
|
|
||||||
|
let hasFailedSends = false;
|
||||||
|
|
||||||
const newSendStateByConversationId = Object.keys(
|
const newSendStateByConversationId = Object.keys(
|
||||||
oldSendStateByConversationId
|
oldSendStateByConversationId
|
||||||
).reduce((acc, conversationId) => {
|
).reduce((acc, conversationId) => {
|
||||||
|
@ -481,6 +483,8 @@ export async function sendStory(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasFailedSends = true;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[conversationId]: sendStateReducer(oldSendState, {
|
[conversationId]: sendStateReducer(oldSendState, {
|
||||||
|
@ -490,6 +494,10 @@ export async function sendStory(
|
||||||
};
|
};
|
||||||
}, {} as SendStateByConversationId);
|
}, {} as SendStateByConversationId);
|
||||||
|
|
||||||
|
if (hasFailedSends) {
|
||||||
|
message.notifyStorySendFailed();
|
||||||
|
}
|
||||||
|
|
||||||
if (isEqual(oldSendStateByConversationId, newSendStateByConversationId)) {
|
if (isEqual(oldSendStateByConversationId, newSendStateByConversationId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1467,6 +1467,26 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.notifyStorySendFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public notifyStorySendFailed(): void {
|
||||||
|
if (!isStory(this.attributes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationService.add({
|
||||||
|
conversationId: this.get('conversationId'),
|
||||||
|
storyId: this.id,
|
||||||
|
messageId: this.id,
|
||||||
|
senderTitle:
|
||||||
|
this.getConversation()?.getTitle() ?? window.i18n('Stories__mine'),
|
||||||
|
message: this.hasSuccessfulDelivery()
|
||||||
|
? window.i18n('icu:Stories__failed-send--partial')
|
||||||
|
: window.i18n('icu:Stories__failed-send--full'),
|
||||||
|
isExpiringMessage: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOutgoingErrors(incomingIdentifier: string): CustomError {
|
removeOutgoingErrors(incomingIdentifier: string): CustomError {
|
||||||
|
@ -1619,6 +1639,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
updatedAt: Date.now(),
|
updatedAt: Date.now(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.notifyStorySendFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import * as log from '../../logging/log';
|
||||||
import { SIGNAL_ACI } from '../../types/SignalConversation';
|
import { SIGNAL_ACI } from '../../types/SignalConversation';
|
||||||
import dataInterface from '../../sql/Client';
|
import dataInterface from '../../sql/Client';
|
||||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||||
|
import { SendStatus } from '../../messages/MessageSendState';
|
||||||
import { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
|
import { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
|
||||||
import { StoryViewDirectionType, StoryViewModeType } from '../../types/Stories';
|
import { StoryViewDirectionType, StoryViewModeType } from '../../types/Stories';
|
||||||
import { assertDev, strictAssert } from '../../util/assert';
|
import { assertDev, strictAssert } from '../../util/assert';
|
||||||
|
@ -987,6 +988,10 @@ const viewStory: ViewStoryActionCreatorType = (
|
||||||
|
|
||||||
// Go directly to the storyId selected
|
// Go directly to the storyId selected
|
||||||
if (!viewDirection) {
|
if (!viewDirection) {
|
||||||
|
const hasFailedSend = Object.values(
|
||||||
|
story.sendStateByConversationId || {}
|
||||||
|
).some(({ status }) => status === SendStatus.Failed);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: VIEW_STORY,
|
type: VIEW_STORY,
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -995,7 +1000,7 @@ const viewStory: ViewStoryActionCreatorType = (
|
||||||
numStories,
|
numStories,
|
||||||
storyViewMode,
|
storyViewMode,
|
||||||
unviewedStoryConversationIdsSorted,
|
unviewedStoryConversationIdsSorted,
|
||||||
viewTarget,
|
viewTarget: hasFailedSend ? undefined : viewTarget,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -576,3 +576,16 @@ export const getHasAllStoriesUnmuted = createSelector(
|
||||||
getStoriesState,
|
getStoriesState,
|
||||||
({ hasAllStoriesUnmuted }): boolean => hasAllStoriesUnmuted
|
({ hasAllStoriesUnmuted }): boolean => hasAllStoriesUnmuted
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getHasAnyFailedStorySends = createSelector(
|
||||||
|
getStoriesState,
|
||||||
|
({ lastOpenedAtTimestamp, stories }): boolean => {
|
||||||
|
return stories.some(
|
||||||
|
story =>
|
||||||
|
story.timestamp > (lastOpenedAtTimestamp || 0) &&
|
||||||
|
Object.values(story.sendStateByConversationId || {}).some(
|
||||||
|
({ status }) => status === SendStatus.Failed
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -17,7 +17,10 @@ import {
|
||||||
} from '../selectors/user';
|
} from '../selectors/user';
|
||||||
import { getMe } from '../selectors/conversations';
|
import { getMe } from '../selectors/conversations';
|
||||||
import { getStoriesEnabled } from '../selectors/items';
|
import { getStoriesEnabled } from '../selectors/items';
|
||||||
import { getStoriesNotificationCount } from '../selectors/stories';
|
import {
|
||||||
|
getStoriesNotificationCount,
|
||||||
|
getHasAnyFailedStorySends,
|
||||||
|
} from '../selectors/stories';
|
||||||
|
|
||||||
const mapStateToProps = (state: StateType) => {
|
const mapStateToProps = (state: StateType) => {
|
||||||
const me = getMe(state);
|
const me = getMe(state);
|
||||||
|
@ -32,6 +35,7 @@ const mapStateToProps = (state: StateType) => {
|
||||||
badge: getPreferredBadgeSelector(state)(me.badges),
|
badge: getPreferredBadgeSelector(state)(me.badges),
|
||||||
theme: getTheme(state),
|
theme: getTheme(state),
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
|
hasFailedStorySends: getHasAnyFailedStorySends(state),
|
||||||
unreadStoriesCount: getStoriesNotificationCount(state),
|
unreadStoriesCount: getStoriesNotificationCount(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue