Update nav tab badges, fix several call tabs issues
This commit is contained in:
parent
ed6ffb695a
commit
9c7dc22a23
43 changed files with 1095 additions and 936 deletions
|
@ -17,11 +17,13 @@ import * as Errors from '../../types/errors';
|
|||
export type CallHistoryState = ReadonlyDeep<{
|
||||
// This informs the app that underlying call history data has changed.
|
||||
edition: number;
|
||||
unreadCount: number;
|
||||
callHistoryByCallId: Record<string, CallHistoryDetails>;
|
||||
}>;
|
||||
|
||||
const CALL_HISTORY_CACHE = 'callHistory/CACHE';
|
||||
const CALL_HISTORY_RESET = 'callHistory/RESET';
|
||||
const CALL_HISTORY_UPDATE_UNREAD = 'callHistory/UPDATE_UNREAD';
|
||||
|
||||
export type CallHistoryCache = ReadonlyDeep<{
|
||||
type: typeof CALL_HISTORY_CACHE;
|
||||
|
@ -32,17 +34,58 @@ export type CallHistoryReset = ReadonlyDeep<{
|
|||
type: typeof CALL_HISTORY_RESET;
|
||||
}>;
|
||||
|
||||
export type CallHistoryUpdateUnread = ReadonlyDeep<{
|
||||
type: typeof CALL_HISTORY_UPDATE_UNREAD;
|
||||
payload: number;
|
||||
}>;
|
||||
|
||||
export type CallHistoryAction = ReadonlyDeep<
|
||||
CallHistoryCache | CallHistoryReset
|
||||
CallHistoryCache | CallHistoryReset | CallHistoryUpdateUnread
|
||||
>;
|
||||
|
||||
export function getEmptyState(): CallHistoryState {
|
||||
return {
|
||||
edition: 0,
|
||||
unreadCount: 0,
|
||||
callHistoryByCallId: {},
|
||||
};
|
||||
}
|
||||
|
||||
function updateCallHistoryUnreadCount(): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
CallHistoryUpdateUnread
|
||||
> {
|
||||
return async dispatch => {
|
||||
try {
|
||||
const unreadCount = await window.Signal.Data.getCallHistoryUnreadCount();
|
||||
dispatch({ type: CALL_HISTORY_UPDATE_UNREAD, payload: unreadCount });
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'Error updating call history unread count',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function markCallHistoryRead(
|
||||
conversationId: string,
|
||||
callId: string
|
||||
): ThunkAction<void, RootStateType, unknown, CallHistoryUpdateUnread> {
|
||||
return async dispatch => {
|
||||
try {
|
||||
await window.Signal.Data.markCallHistoryRead(callId);
|
||||
await window.ConversationController.get(conversationId)?.updateUnread();
|
||||
} catch (error) {
|
||||
log.error('Error marking call history read', Errors.toLogFormat(error));
|
||||
} finally {
|
||||
dispatch(updateCallHistoryUnreadCount());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function cacheCallHistory(callHistory: CallHistoryDetails): CallHistoryCache {
|
||||
return {
|
||||
type: CALL_HISTORY_CACHE,
|
||||
|
@ -65,6 +108,7 @@ function clearAllCallHistory(): ThunkAction<
|
|||
} finally {
|
||||
// Just force a reset, even if the clear failed.
|
||||
dispatch({ type: CALL_HISTORY_RESET });
|
||||
dispatch(updateCallHistoryUnreadCount());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -72,6 +116,8 @@ function clearAllCallHistory(): ThunkAction<
|
|||
export const actions = {
|
||||
cacheCallHistory,
|
||||
clearAllCallHistory,
|
||||
updateCallHistoryUnreadCount,
|
||||
markCallHistoryRead,
|
||||
};
|
||||
|
||||
export const useCallHistoryActions = (): BoundActionCreatorsMapObject<
|
||||
|
@ -93,6 +139,11 @@ export function reducer(
|
|||
[action.payload.callId]: action.payload,
|
||||
},
|
||||
};
|
||||
case CALL_HISTORY_UPDATE_UNREAD:
|
||||
return {
|
||||
...state,
|
||||
unreadCount: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -29,3 +29,10 @@ export const getCallHistorySelector = createSelector(
|
|||
};
|
||||
}
|
||||
);
|
||||
|
||||
export const getCallHistoryUnreadCount = createSelector(
|
||||
getCallHistory,
|
||||
callHistory => {
|
||||
return callHistory.unreadCount;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -66,6 +66,10 @@ import type { HasStories } from '../../types/Stories';
|
|||
import { getHasStoriesSelector } from './stories2';
|
||||
import { canEditMessage } from '../../util/canEditMessage';
|
||||
import { isOutgoing } from '../../messages/helpers';
|
||||
import {
|
||||
countAllConversationsUnreadStats,
|
||||
type UnreadStats,
|
||||
} from '../../util/countUnreadStats';
|
||||
|
||||
export type ConversationWithStoriesType = ConversationType & {
|
||||
hasStories?: HasStories;
|
||||
|
@ -532,37 +536,12 @@ export const getAllGroupsWithInviteAccess = createSelector(
|
|||
})
|
||||
);
|
||||
|
||||
export type UnreadStats = Readonly<{
|
||||
unreadCount: number;
|
||||
unreadMentionsCount: number;
|
||||
markedUnread: boolean;
|
||||
}>;
|
||||
|
||||
export const getAllConversationsUnreadStats = createSelector(
|
||||
getLeftPaneLists,
|
||||
(leftPaneLists: LeftPaneLists): UnreadStats => {
|
||||
let unreadCount = 0;
|
||||
let unreadMentionsCount = 0;
|
||||
let markedUnread = false;
|
||||
|
||||
function count(conversations: ReadonlyArray<ConversationType>) {
|
||||
conversations.forEach(conversation => {
|
||||
if (conversation.unreadCount != null) {
|
||||
unreadCount += conversation.unreadCount;
|
||||
}
|
||||
if (conversation.unreadMentionsCount != null) {
|
||||
unreadMentionsCount += conversation.unreadMentionsCount;
|
||||
}
|
||||
if (conversation.markedUnread) {
|
||||
markedUnread = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
count(leftPaneLists.pinnedConversations);
|
||||
count(leftPaneLists.conversations);
|
||||
|
||||
return { unreadCount, unreadMentionsCount, markedUnread };
|
||||
getAllConversations,
|
||||
(conversations): UnreadStats => {
|
||||
return countAllConversationsUnreadStats(conversations, {
|
||||
includeMuted: false,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import type { StateType } from '../reducer';
|
||||
import type { NavStateType } from '../ducks/nav';
|
||||
import { getAllConversationsUnreadStats } from './conversations';
|
||||
import { getStoriesNotificationCount } from './stories';
|
||||
import type { UnreadStats } from '../../util/countUnreadStats';
|
||||
|
||||
function getNav(state: StateType): NavStateType {
|
||||
return state.nav;
|
||||
|
@ -12,3 +15,17 @@ function getNav(state: StateType): NavStateType {
|
|||
export const getSelectedNavTab = createSelector(getNav, nav => {
|
||||
return nav.selectedNavTab;
|
||||
});
|
||||
|
||||
export const getAppUnreadStats = createSelector(
|
||||
getAllConversationsUnreadStats,
|
||||
getStoriesNotificationCount,
|
||||
(conversationsUnreadStats, storiesNotificationCount): UnreadStats => {
|
||||
return {
|
||||
// Note: Conversation unread stats includes the call history unread count.
|
||||
unreadCount:
|
||||
conversationsUnreadStats.unreadCount + storiesNotificationCount,
|
||||
unreadMentionsCount: conversationsUnreadStats.unreadMentionsCount,
|
||||
markedUnread: conversationsUnreadStats.markedUnread,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -42,3 +42,8 @@ export const isOSUnsupported = createSelector(
|
|||
getUpdatesState,
|
||||
({ dialogType }) => dialogType === DialogType.UnsupportedOS
|
||||
);
|
||||
|
||||
export const getHasPendingUpdate = createSelector(
|
||||
getUpdatesState,
|
||||
({ didSnooze }) => didSnooze === true
|
||||
);
|
||||
|
|
|
@ -27,6 +27,9 @@ import { useCallingActions } from '../ducks/calling';
|
|||
import { getActiveCallState } from '../selectors/calling';
|
||||
import { useCallHistoryActions } from '../ducks/callHistory';
|
||||
import { getCallHistoryEdition } from '../selectors/callHistory';
|
||||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
||||
import { getAppUnreadStats } from '../selectors/nav';
|
||||
|
||||
function getCallHistoryFilter(
|
||||
allConversations: Array<ConversationType>,
|
||||
|
@ -91,11 +94,16 @@ export function SmartCallsTab(): JSX.Element {
|
|||
const activeCall = useSelector(getActiveCallState);
|
||||
const callHistoryEdition = useSelector(getCallHistoryEdition);
|
||||
|
||||
const hasPendingUpdate = useSelector(getHasPendingUpdate);
|
||||
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
|
||||
const appUnreadStats = useSelector(getAppUnreadStats);
|
||||
|
||||
const {
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
} = useCallingActions();
|
||||
const { clearAllCallHistory: clearCallHistory } = useCallHistoryActions();
|
||||
const { clearAllCallHistory: clearCallHistory, markCallHistoryRead } =
|
||||
useCallHistoryActions();
|
||||
|
||||
const getCallHistoryGroupsCount = useCallback(
|
||||
async (options: CallHistoryFilterOptions) => {
|
||||
|
@ -145,12 +153,16 @@ export function SmartCallsTab(): JSX.Element {
|
|||
<CallsTab
|
||||
activeCall={activeCall}
|
||||
allConversations={allConversations}
|
||||
appUnreadStats={appUnreadStats}
|
||||
getConversation={getConversation}
|
||||
getCallHistoryGroupsCount={getCallHistoryGroupsCount}
|
||||
getCallHistoryGroups={getCallHistoryGroups}
|
||||
hasFailedStorySends={hasFailedStorySends}
|
||||
hasPendingUpdate={hasPendingUpdate}
|
||||
i18n={i18n}
|
||||
navTabsCollapsed={navTabsCollapsed}
|
||||
onClearCallHistory={clearCallHistory}
|
||||
onMarkCallHistoryRead={markCallHistoryRead}
|
||||
onToggleNavTabsCollapse={toggleNavTabsCollapse}
|
||||
onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation}
|
||||
onOutgoingVideoCallInConversation={onOutgoingVideoCallInConversation}
|
||||
|
|
|
@ -20,6 +20,9 @@ import { showToast } from '../../util/showToast';
|
|||
import { ToastStickerPackInstallFailed } from '../../components/ToastStickerPackInstallFailed';
|
||||
import { getNavTabsCollapsed } from '../selectors/items';
|
||||
import { useItemsActions } from '../ducks/items';
|
||||
import { getHasAnyFailedStorySends } from '../selectors/stories';
|
||||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getAppUnreadStats } from '../selectors/nav';
|
||||
|
||||
function renderConversationView() {
|
||||
return <SmartConversationView />;
|
||||
|
@ -36,6 +39,10 @@ function renderMiniPlayer(options: { shouldFlow: boolean }) {
|
|||
export function SmartChatsTab(): JSX.Element {
|
||||
const i18n = useSelector(getIntl);
|
||||
const navTabsCollapsed = useSelector(getNavTabsCollapsed);
|
||||
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
|
||||
const hasPendingUpdate = useSelector(getHasPendingUpdate);
|
||||
const appUnreadStats = useSelector(getAppUnreadStats);
|
||||
|
||||
const { selectedConversationId, targetedMessage, targetedMessageSource } =
|
||||
useSelector<StateType, ConversationsStateType>(
|
||||
state => state.conversations
|
||||
|
@ -137,7 +144,10 @@ export function SmartChatsTab(): JSX.Element {
|
|||
|
||||
return (
|
||||
<ChatsTab
|
||||
appUnreadStats={appUnreadStats}
|
||||
i18n={i18n}
|
||||
hasFailedStorySends={hasFailedStorySends}
|
||||
hasPendingUpdate={hasPendingUpdate}
|
||||
navTabsCollapsed={navTabsCollapsed}
|
||||
onToggleNavTabsCollapse={toggleNavTabsCollapse}
|
||||
prevConversationId={prevConversationId}
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
getMe,
|
||||
} from '../selectors/conversations';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import type { StateType } from '../reducer';
|
||||
import {
|
||||
getHasAnyFailedStorySends,
|
||||
getStoriesNotificationCount,
|
||||
|
@ -23,6 +22,8 @@ import { getStoriesEnabled } from '../selectors/items';
|
|||
import { getSelectedNavTab } from '../selectors/nav';
|
||||
import type { NavTab } from '../ducks/nav';
|
||||
import { useNavActions } from '../ducks/nav';
|
||||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getCallHistoryUnreadCount } from '../selectors/callHistory';
|
||||
|
||||
export type SmartNavTabsProps = Readonly<{
|
||||
navTabsCollapsed: boolean;
|
||||
|
@ -48,11 +49,9 @@ export function SmartNavTabs({
|
|||
const storiesEnabled = useSelector(getStoriesEnabled);
|
||||
const unreadConversationsStats = useSelector(getAllConversationsUnreadStats);
|
||||
const unreadStoriesCount = useSelector(getStoriesNotificationCount);
|
||||
const unreadCallsCount = useSelector(getCallHistoryUnreadCount);
|
||||
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
|
||||
|
||||
const hasPendingUpdate = useSelector((state: StateType) => {
|
||||
return state.updates.didSnooze;
|
||||
});
|
||||
const hasPendingUpdate = useSelector(getHasPendingUpdate);
|
||||
|
||||
const { toggleProfileEditor } = useGlobalModalActions();
|
||||
const { startUpdate } = useUpdatesActions();
|
||||
|
@ -87,6 +86,7 @@ export function SmartNavTabs({
|
|||
selectedNavTab={selectedNavTab}
|
||||
storiesEnabled={storiesEnabled}
|
||||
theme={theme}
|
||||
unreadCallsCount={unreadCallsCount}
|
||||
unreadConversationsStats={unreadConversationsStats}
|
||||
unreadStoriesCount={unreadStoriesCount}
|
||||
/>
|
||||
|
|
|
@ -45,6 +45,7 @@ function mapStateToProps(
|
|||
firstName,
|
||||
familyName,
|
||||
id: conversationId,
|
||||
phoneNumber,
|
||||
username,
|
||||
} = getMe(state);
|
||||
const recentEmojis = selectRecentEmojis(state);
|
||||
|
@ -74,6 +75,7 @@ function mapStateToProps(
|
|||
isUsernameFlagEnabled,
|
||||
recentEmojis,
|
||||
skinTone,
|
||||
phoneNumber,
|
||||
userAvatarData,
|
||||
username,
|
||||
usernameEditState,
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
} from '../selectors/items';
|
||||
import {
|
||||
getAddStoryData,
|
||||
getHasAnyFailedStorySends,
|
||||
getSelectedStoryData,
|
||||
getStories,
|
||||
} from '../selectors/stories';
|
||||
|
@ -30,6 +31,8 @@ import { useStoriesActions } from '../ducks/stories';
|
|||
import { useToastActions } from '../ducks/toast';
|
||||
import { useAudioPlayerActions } from '../ducks/audioPlayer';
|
||||
import { useItemsActions } from '../ducks/items';
|
||||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getAppUnreadStats } from '../selectors/nav';
|
||||
|
||||
function renderStoryCreator(): JSX.Element {
|
||||
return <SmartStoryCreator />;
|
||||
|
@ -66,6 +69,9 @@ export function SmartStoriesTab(): JSX.Element | null {
|
|||
);
|
||||
|
||||
const hasViewReceiptSetting = useSelector(getHasStoryViewReceiptSetting);
|
||||
const hasPendingUpdate = useSelector(getHasPendingUpdate);
|
||||
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
|
||||
const appUnreadStats = useSelector(getAppUnreadStats);
|
||||
|
||||
const remoteConfig = useSelector(getRemoteConfig);
|
||||
const maxAttachmentSizeInKb = getMaximumAttachmentSizeInKb(
|
||||
|
@ -92,8 +98,11 @@ export function SmartStoriesTab(): JSX.Element | null {
|
|||
|
||||
return (
|
||||
<StoriesTab
|
||||
appUnreadStats={appUnreadStats}
|
||||
addStoryData={addStoryData}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
hasFailedStorySends={hasFailedStorySends}
|
||||
hasPendingUpdate={hasPendingUpdate}
|
||||
hiddenStories={hiddenStories}
|
||||
i18n={i18n}
|
||||
maxAttachmentSizeInKb={maxAttachmentSizeInKb}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue