mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-20 02:08:57 +00:00
Chat folder fixes
Co-authored-by: Jamie <113370520+jamiebuilds-signal@users.noreply.github.com>
This commit is contained in:
@@ -5826,6 +5826,12 @@ button.module-calling-participants-list__contact {
|
||||
}
|
||||
}
|
||||
|
||||
.module-left-pane__dialogs {
|
||||
& + .module-left-pane__chatFolders {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.module-left-pane__archive-helper-text {
|
||||
@include mixins.font-body-2;
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ function getChatFolderIconName(
|
||||
return chatFolder.folderType === ChatFolderType.ALL ? 'message' : 'folder';
|
||||
}
|
||||
|
||||
// Needed to conditionally apply margin after network warning/update available
|
||||
const LEFT_PANE_CHAT_FOLDERS_CLASS_NAME = 'module-left-pane__chatFolders';
|
||||
|
||||
export function LeftPaneChatFolders(
|
||||
props: LeftPaneChatFoldersProps
|
||||
): JSX.Element | null {
|
||||
@@ -116,7 +119,7 @@ export function LeftPaneChatFolders(
|
||||
|
||||
if (props.navSidebarWidthBreakpoint === WidthBreakpoint.Narrow) {
|
||||
return (
|
||||
<div className={tw('px-2')}>
|
||||
<div className={tw(LEFT_PANE_CHAT_FOLDERS_CLASS_NAME, 'px-2')}>
|
||||
<AxoSelect.Root
|
||||
value={props.selectedChatFolder?.id ?? null}
|
||||
onValueChange={handleValueChange}
|
||||
|
||||
@@ -9,25 +9,28 @@ import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions.s
|
||||
import { useBoundActions } from '../../hooks/useBoundActions.std.js';
|
||||
import {
|
||||
ChatFolderParamsSchema,
|
||||
isConversationInChatFolder,
|
||||
type ChatFolder,
|
||||
type ChatFolderId,
|
||||
type ChatFolderParams,
|
||||
} from '../../types/ChatFolder.std.js';
|
||||
import { getCurrentChatFolders } from '../selectors/chatFolders.std.js';
|
||||
import {
|
||||
getCurrentChatFolders,
|
||||
getSelectedChatFolder,
|
||||
} from '../selectors/chatFolders.std.js';
|
||||
import { DataReader, DataWriter } from '../../sql/Client.preload.js';
|
||||
import { storageServiceUploadJob } from '../../services/storage.preload.js';
|
||||
import { parseStrict } from '../../util/schemas.std.js';
|
||||
import { chatFolderCleanupService } from '../../services/expiring/chatFolderCleanupService.preload.js';
|
||||
import { drop } from '../../util/drop.std.js';
|
||||
import {
|
||||
TARGETED_CONVERSATION_CHANGED,
|
||||
type TargetedConversationChangedActionType,
|
||||
} from './conversations.preload.js';
|
||||
import type { ShowToastActionType } from './toast.preload.js';
|
||||
import { showToast } from './toast.preload.js';
|
||||
import { ToastType } from '../../types/Toast.dom.js';
|
||||
import type { CurrentChatFolder } from '../../types/CurrentChatFolders.std.js';
|
||||
import { CurrentChatFolders } from '../../types/CurrentChatFolders.std.js';
|
||||
import { getConversationLookup } from '../selectors/conversations.dom.js';
|
||||
import { getOwn } from '../../util/getOwn.std.js';
|
||||
import { strictAssert } from '../../util/assert.std.js';
|
||||
|
||||
export type ChatFoldersState = ReadonlyDeep<{
|
||||
currentChatFolders: CurrentChatFolders;
|
||||
@@ -37,21 +40,30 @@ export type ChatFoldersState = ReadonlyDeep<{
|
||||
|
||||
const CHAT_FOLDER_RECORD_REPLACE_ALL =
|
||||
'chatFolders/CHAT_FOLDER_RECORD_REPLACE_ALL';
|
||||
const CHAT_FOLDER_CHANGE_SELECTED_CHAT_FOLDER_ID =
|
||||
'chatFolders/CHANGE_SELECTED_CHAT_FOLDER_ID';
|
||||
const CHAT_FOLDER_UPDATE_SELECTED_CHAT_FOLDER_ID =
|
||||
'chatFolders/UPDATE_SELECTED_CHAT_FOLDER_ID';
|
||||
const CHAT_FOLDER_UPDATE_STABLE_SELECTED_CONVERSATION_ID_IN_CHAT_FOLDER =
|
||||
'chatFolders/CHAT_FOLDER_UPDATE_STABLE_SELECTED_CONVERSATION_ID_IN_CHAT_FOLDER';
|
||||
|
||||
export type ChatFolderRecordReplaceAll = ReadonlyDeep<{
|
||||
type: typeof CHAT_FOLDER_RECORD_REPLACE_ALL;
|
||||
payload: CurrentChatFolders;
|
||||
}>;
|
||||
|
||||
export type ChatFolderChangeSelectedChatFolderId = ReadonlyDeep<{
|
||||
type: typeof CHAT_FOLDER_CHANGE_SELECTED_CHAT_FOLDER_ID;
|
||||
export type ChatFolderUpdateSelectedChatFolderId = ReadonlyDeep<{
|
||||
type: typeof CHAT_FOLDER_UPDATE_SELECTED_CHAT_FOLDER_ID;
|
||||
payload: ChatFolderId | null;
|
||||
}>;
|
||||
|
||||
export type ChatFolderUpdateStableConversationIdInChatFolder = ReadonlyDeep<{
|
||||
type: typeof CHAT_FOLDER_UPDATE_STABLE_SELECTED_CONVERSATION_ID_IN_CHAT_FOLDER;
|
||||
payload: string | null;
|
||||
}>;
|
||||
|
||||
export type ChatFolderAction = ReadonlyDeep<
|
||||
ChatFolderRecordReplaceAll | ChatFolderChangeSelectedChatFolderId
|
||||
| ChatFolderRecordReplaceAll
|
||||
| ChatFolderUpdateSelectedChatFolderId
|
||||
| ChatFolderUpdateStableConversationIdInChatFolder
|
||||
>;
|
||||
|
||||
export function getEmptyState(): ChatFoldersState {
|
||||
@@ -198,15 +210,24 @@ function updateChatFoldersPositions(
|
||||
};
|
||||
}
|
||||
|
||||
function updateSelectedChangeFolderId(
|
||||
function updateSelectedChatFolderId(
|
||||
chatFolderId: ChatFolderId | null
|
||||
): ChatFolderChangeSelectedChatFolderId {
|
||||
): ChatFolderUpdateSelectedChatFolderId {
|
||||
return {
|
||||
type: CHAT_FOLDER_CHANGE_SELECTED_CHAT_FOLDER_ID,
|
||||
type: CHAT_FOLDER_UPDATE_SELECTED_CHAT_FOLDER_ID,
|
||||
payload: chatFolderId,
|
||||
};
|
||||
}
|
||||
|
||||
function updateStableSelectedConversationIdInChatFolder(
|
||||
conversationId: string | null
|
||||
): ChatFolderUpdateStableConversationIdInChatFolder {
|
||||
return {
|
||||
type: CHAT_FOLDER_UPDATE_STABLE_SELECTED_CONVERSATION_ID_IN_CHAT_FOLDER,
|
||||
payload: conversationId,
|
||||
};
|
||||
}
|
||||
|
||||
function updateChatFolderToggleChat(
|
||||
chatFolderId: ChatFolderId,
|
||||
conversationId: string,
|
||||
@@ -242,6 +263,47 @@ function updateChatFolderToggleChat(
|
||||
};
|
||||
}
|
||||
|
||||
export function updateChatFolderStateOnTargetConversationChanged(
|
||||
conversationId: string | undefined
|
||||
): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
| ChatFolderUpdateSelectedChatFolderId
|
||||
| ChatFolderUpdateStableConversationIdInChatFolder
|
||||
> {
|
||||
return async (dispatch, getState) => {
|
||||
if (conversationId == null) {
|
||||
// Clear out the stable conversation id since it has been closed.
|
||||
dispatch(updateStableSelectedConversationIdInChatFolder(null));
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const selectedChatFolder = getSelectedChatFolder(state);
|
||||
if (selectedChatFolder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conversationLookup = getConversationLookup(state);
|
||||
const conversation = getOwn(conversationLookup, conversationId);
|
||||
strictAssert(conversation != null, 'Target conversation not found');
|
||||
|
||||
if (isConversationInChatFolder(selectedChatFolder, conversation)) {
|
||||
// Make sure the targetted conversation doesn't appear from the chat folder
|
||||
// while its open (in case it gets marked read for example).
|
||||
dispatch(updateStableSelectedConversationIdInChatFolder(conversationId));
|
||||
return;
|
||||
}
|
||||
|
||||
const currentChatFolders = getCurrentChatFolders(state);
|
||||
const { currentAllChatFolder } = currentChatFolders;
|
||||
|
||||
dispatch(updateSelectedChatFolderId(currentAllChatFolder?.id ?? null));
|
||||
dispatch(updateStableSelectedConversationIdInChatFolder(conversationId));
|
||||
};
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
refetchChatFolders,
|
||||
createChatFolder,
|
||||
@@ -249,7 +311,7 @@ export const actions = {
|
||||
updateChatFolder,
|
||||
deleteChatFolder,
|
||||
updateChatFoldersPositions,
|
||||
updateSelectedChangeFolderId,
|
||||
updateSelectedChatFolderId,
|
||||
updateChatFolderToggleChat,
|
||||
};
|
||||
|
||||
@@ -291,8 +353,11 @@ function toNextChatFoldersState(
|
||||
);
|
||||
|
||||
// Ensure `stableSelectedConversationIdInChatFolder`
|
||||
// is reset if `selectedChatFolderId` changes
|
||||
if (nextState.selectedChatFolderId !== prevState.selectedChatFolderId) {
|
||||
// is reset if `selectedChatFolderId` is null or has been changed
|
||||
if (
|
||||
nextState.selectedChatFolderId == null ||
|
||||
nextState.selectedChatFolderId !== prevState.selectedChatFolderId
|
||||
) {
|
||||
nextState.stableSelectedConversationIdInChatFolder = null;
|
||||
}
|
||||
|
||||
@@ -301,21 +366,20 @@ function toNextChatFoldersState(
|
||||
|
||||
export function reducer(
|
||||
state: ChatFoldersState = getEmptyState(),
|
||||
action: ChatFolderAction | TargetedConversationChangedActionType
|
||||
action: ChatFolderAction
|
||||
): ChatFoldersState {
|
||||
switch (action.type) {
|
||||
case CHAT_FOLDER_RECORD_REPLACE_ALL:
|
||||
return toNextChatFoldersState(state, {
|
||||
currentChatFolders: action.payload,
|
||||
});
|
||||
case CHAT_FOLDER_CHANGE_SELECTED_CHAT_FOLDER_ID:
|
||||
case CHAT_FOLDER_UPDATE_SELECTED_CHAT_FOLDER_ID:
|
||||
return toNextChatFoldersState(state, {
|
||||
selectedChatFolderId: action.payload,
|
||||
});
|
||||
case TARGETED_CONVERSATION_CHANGED:
|
||||
case CHAT_FOLDER_UPDATE_STABLE_SELECTED_CONVERSATION_ID_IN_CHAT_FOLDER:
|
||||
return toNextChatFoldersState(state, {
|
||||
stableSelectedConversationIdInChatFolder:
|
||||
action.payload.conversationId ?? null,
|
||||
stableSelectedConversationIdInChatFolder: action.payload,
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
|
||||
@@ -241,6 +241,7 @@ import { isConversationUnread } from '../../util/isConversationUnread.std.js';
|
||||
import { CurrentChatFolders } from '../../types/CurrentChatFolders.std.js';
|
||||
import { itemStorage } from '../../textsecure/Storage.preload.js';
|
||||
import { enqueuePollVoteForSend as enqueuePollVoteForSendHelper } from '../../polls/enqueuePollVoteForSend.preload.js';
|
||||
import { updateChatFolderStateOnTargetConversationChanged } from './chatFolders.preload.js';
|
||||
|
||||
const {
|
||||
chunk,
|
||||
@@ -4808,6 +4809,8 @@ function showConversation({
|
||||
conversation?.setMarkedUnread(false);
|
||||
}
|
||||
|
||||
dispatch(updateChatFolderStateOnTargetConversationChanged(conversationId));
|
||||
|
||||
if (conversationId === conversations.selectedConversationId) {
|
||||
if (!conversationId) {
|
||||
return;
|
||||
|
||||
@@ -33,7 +33,7 @@ export const SmartLeftPaneChatFolders = memo(
|
||||
);
|
||||
const location = useSelector(getSelectedLocation);
|
||||
|
||||
const { updateSelectedChangeFolderId } = useChatFolderActions();
|
||||
const { updateSelectedChatFolderId } = useChatFolderActions();
|
||||
const { changeLocation } = useNavActions();
|
||||
const { markChatFolderRead, setChatFolderMuteExpiration } =
|
||||
useConversationsActions();
|
||||
@@ -66,7 +66,7 @@ export const SmartLeftPaneChatFolders = memo(
|
||||
allChatFoldersUnreadStats={allChatFoldersUnreadStats}
|
||||
allChatFoldersMutedStats={allChatFoldersMutedStats}
|
||||
selectedChatFolder={selectedChatFolder}
|
||||
onSelectedChatFolderIdChange={updateSelectedChangeFolderId}
|
||||
onSelectedChatFolderIdChange={updateSelectedChatFolderId}
|
||||
onChatFolderMarkRead={markChatFolderRead}
|
||||
onChatFolderUpdateMute={setChatFolderMuteExpiration}
|
||||
onChatFolderOpenSettings={handleChatFolderOpenSettings}
|
||||
|
||||
@@ -508,7 +508,12 @@ describe('both/state/ducks/conversations', () => {
|
||||
getEmptyRootState,
|
||||
null
|
||||
);
|
||||
const action = dispatch.getCall(0).args[0];
|
||||
const action = dispatch
|
||||
.getCalls()
|
||||
.map(call => call.args[0])
|
||||
.find(a => {
|
||||
return a.type === TARGETED_CONVERSATION_CHANGED;
|
||||
});
|
||||
const nextState = reducer(state, action);
|
||||
|
||||
assert.equal(nextState.selectedConversationId, 'abc123');
|
||||
@@ -531,7 +536,10 @@ describe('both/state/ducks/conversations', () => {
|
||||
conversationId: 'abc123',
|
||||
messageId: 'xyz987',
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
const action = dispatch.getCall(0).args[0];
|
||||
const action = dispatch
|
||||
.getCalls()
|
||||
.map(call => call.args[0])
|
||||
.find(a => a.type === TARGETED_CONVERSATION_CHANGED);
|
||||
const nextState = reducer(state, action);
|
||||
|
||||
assert.equal(nextState.selectedConversationId, 'abc123');
|
||||
@@ -547,7 +555,12 @@ describe('both/state/ducks/conversations', () => {
|
||||
conversationId: 'fake-conversation-id',
|
||||
switchToAssociatedView: true,
|
||||
})(dispatch, getEmptyRootState, null);
|
||||
[action] = dispatch.getCall(0).args;
|
||||
action = dispatch
|
||||
.getCalls()
|
||||
.map(call => call.args[0])
|
||||
.find(a => {
|
||||
return a.type === TARGETED_CONVERSATION_CHANGED;
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the inbox if the conversation is not archived', () => {
|
||||
|
||||
Reference in New Issue
Block a user