Fixes global chat color setting
This commit is contained in:
parent
a6ce00ff37
commit
bd46e3afd6
14 changed files with 250 additions and 122 deletions
|
@ -5454,10 +5454,18 @@
|
||||||
"message": "Reset chat color",
|
"message": "Reset chat color",
|
||||||
"description": "Button label for resetting chat colors"
|
"description": "Button label for resetting chat colors"
|
||||||
},
|
},
|
||||||
|
"ChatColorPicker__resetDefault": {
|
||||||
|
"message": "Reset Chat Colors",
|
||||||
|
"description": "Confirmation dialog title for resetting all chat colors or only the global default one"
|
||||||
|
},
|
||||||
"ChatColorPicker__resetAll": {
|
"ChatColorPicker__resetAll": {
|
||||||
"message": "Reset all chat colors",
|
"message": "Reset all chat colors",
|
||||||
"description": "Button label for resetting all chat colors"
|
"description": "Button label for resetting all chat colors"
|
||||||
},
|
},
|
||||||
|
"ChatColorPicker__confirm-reset-default": {
|
||||||
|
"message": "Reset default",
|
||||||
|
"description": "Button label for resetting only global chat color"
|
||||||
|
},
|
||||||
"ChatColorPicker__confirm-reset": {
|
"ChatColorPicker__confirm-reset": {
|
||||||
"message": "Reset",
|
"message": "Reset",
|
||||||
"description": "Confirm button label for resetting chat colors"
|
"description": "Confirm button label for resetting chat colors"
|
||||||
|
|
|
@ -412,6 +412,12 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
|
if (!window.storage.get('defaultConversationColor')) {
|
||||||
|
window.storage.put('defaultConversationColor', {
|
||||||
|
color: 'ultramarine',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
cleanupSessionResets();
|
cleanupSessionResets();
|
||||||
const retryPlaceholders = new window.Signal.Util.RetryPlaceholders();
|
const retryPlaceholders = new window.Signal.Util.RetryPlaceholders();
|
||||||
window.Signal.Services.retryPlaceholders = retryPlaceholders;
|
window.Signal.Services.retryPlaceholders = retryPlaceholders;
|
||||||
|
|
|
@ -32,6 +32,7 @@ const createProps = (): PropsType => ({
|
||||||
removeCustomColor: action('removeCustomColor'),
|
removeCustomColor: action('removeCustomColor'),
|
||||||
removeCustomColorOnConversations: action('removeCustomColorOnConversations'),
|
removeCustomColorOnConversations: action('removeCustomColorOnConversations'),
|
||||||
resetAllChatColors: action('resetAllChatColors'),
|
resetAllChatColors: action('resetAllChatColors'),
|
||||||
|
resetDefaultChatColor: action('resetDefaultChatColor'),
|
||||||
selectedColor: select('selectedColor', ConversationColors, 'basil' as const),
|
selectedColor: select('selectedColor', ConversationColors, 'basil' as const),
|
||||||
selectedCustomColor: {},
|
selectedCustomColor: {},
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,7 +27,7 @@ export type PropsDataType = {
|
||||||
customColors?: Record<string, CustomColorType>;
|
customColors?: Record<string, CustomColorType>;
|
||||||
getConversationsWithCustomColor: (colorId: string) => Array<ConversationType>;
|
getConversationsWithCustomColor: (colorId: string) => Array<ConversationType>;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isInModal?: boolean;
|
isGlobal?: boolean;
|
||||||
onChatColorReset?: () => unknown;
|
onChatColorReset?: () => unknown;
|
||||||
onSelectColor: (
|
onSelectColor: (
|
||||||
color: ConversationColorType,
|
color: ConversationColorType,
|
||||||
|
@ -46,6 +46,7 @@ type PropsActionType = {
|
||||||
removeCustomColor: (colorId: string) => unknown;
|
removeCustomColor: (colorId: string) => unknown;
|
||||||
removeCustomColorOnConversations: (colorId: string) => unknown;
|
removeCustomColorOnConversations: (colorId: string) => unknown;
|
||||||
resetAllChatColors: () => unknown;
|
resetAllChatColors: () => unknown;
|
||||||
|
resetDefaultChatColor: () => unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsType = PropsDataType & PropsActionType;
|
export type PropsType = PropsDataType & PropsActionType;
|
||||||
|
@ -56,16 +57,18 @@ export const ChatColorPicker = ({
|
||||||
editCustomColor,
|
editCustomColor,
|
||||||
getConversationsWithCustomColor,
|
getConversationsWithCustomColor,
|
||||||
i18n,
|
i18n,
|
||||||
isInModal = false,
|
isGlobal = false,
|
||||||
onChatColorReset,
|
onChatColorReset,
|
||||||
onSelectColor,
|
onSelectColor,
|
||||||
removeCustomColor,
|
removeCustomColor,
|
||||||
removeCustomColorOnConversations,
|
removeCustomColorOnConversations,
|
||||||
resetAllChatColors,
|
resetAllChatColors,
|
||||||
|
resetDefaultChatColor,
|
||||||
selectedColor = ConversationColors[0],
|
selectedColor = ConversationColors[0],
|
||||||
selectedCustomColor,
|
selectedCustomColor,
|
||||||
}: PropsType): JSX.Element => {
|
}: PropsType): JSX.Element => {
|
||||||
const [confirmResetAll, setConfirmResetAll] = useState(false);
|
const [confirmResetAll, setConfirmResetAll] = useState(false);
|
||||||
|
const [confirmResetWhat, setConfirmResetWhat] = useState(false);
|
||||||
const [customColorToEdit, setCustomColorToEdit] = useState<
|
const [customColorToEdit, setCustomColorToEdit] = useState<
|
||||||
CustomColorDataType | undefined
|
CustomColorDataType | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
@ -74,7 +77,7 @@ export const ChatColorPicker = ({
|
||||||
<CustomColorEditorWrapper
|
<CustomColorEditorWrapper
|
||||||
customColorToEdit={customColorToEdit}
|
customColorToEdit={customColorToEdit}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
isInModal={isInModal}
|
isGlobal={isGlobal}
|
||||||
onClose={() => setCustomColorToEdit(undefined)}
|
onClose={() => setCustomColorToEdit(undefined)}
|
||||||
onSave={(color: CustomColorType) => {
|
onSave={(color: CustomColorType) => {
|
||||||
if (customColorToEdit?.id) {
|
if (customColorToEdit?.id) {
|
||||||
|
@ -90,13 +93,39 @@ export const ChatColorPicker = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isInModal && customColorToEdit) {
|
if (isGlobal && customColorToEdit) {
|
||||||
return renderCustomColorEditorWrapper();
|
return renderCustomColorEditorWrapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ChatColorPicker__container">
|
<div className="ChatColorPicker__container">
|
||||||
{customColorToEdit ? renderCustomColorEditorWrapper() : null}
|
{customColorToEdit ? renderCustomColorEditorWrapper() : null}
|
||||||
|
{confirmResetWhat ? (
|
||||||
|
<ConfirmationDialog
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
action: resetDefaultChatColor,
|
||||||
|
style: 'affirmative',
|
||||||
|
text: i18n('ChatColorPicker__confirm-reset-default'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: () => {
|
||||||
|
resetDefaultChatColor();
|
||||||
|
resetAllChatColors();
|
||||||
|
},
|
||||||
|
style: 'affirmative',
|
||||||
|
text: i18n('ChatColorPicker__resetAll'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
i18n={i18n}
|
||||||
|
onClose={() => {
|
||||||
|
setConfirmResetWhat(false);
|
||||||
|
}}
|
||||||
|
title={i18n('ChatColorPicker__resetDefault')}
|
||||||
|
>
|
||||||
|
{i18n('ChatColorPicker__confirm-reset-message')}
|
||||||
|
</ConfirmationDialog>
|
||||||
|
) : null}
|
||||||
{confirmResetAll ? (
|
{confirmResetAll ? (
|
||||||
<ConfirmationDialog
|
<ConfirmationDialog
|
||||||
actions={[
|
actions={[
|
||||||
|
@ -198,7 +227,11 @@ export const ChatColorPicker = ({
|
||||||
<PanelRow
|
<PanelRow
|
||||||
label={i18n('ChatColorPicker__resetAll')}
|
label={i18n('ChatColorPicker__resetAll')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setConfirmResetAll(true);
|
if (isGlobal) {
|
||||||
|
setConfirmResetWhat(true);
|
||||||
|
} else {
|
||||||
|
setConfirmResetAll(true);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -349,7 +382,7 @@ const CustomColorBubble = ({
|
||||||
type CustomColorEditorWrapperPropsType = {
|
type CustomColorEditorWrapperPropsType = {
|
||||||
customColorToEdit?: CustomColorDataType;
|
customColorToEdit?: CustomColorDataType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isInModal: boolean;
|
isGlobal: boolean;
|
||||||
onClose: () => unknown;
|
onClose: () => unknown;
|
||||||
onSave: (color: CustomColorType) => unknown;
|
onSave: (color: CustomColorType) => unknown;
|
||||||
};
|
};
|
||||||
|
@ -357,7 +390,7 @@ type CustomColorEditorWrapperPropsType = {
|
||||||
const CustomColorEditorWrapper = ({
|
const CustomColorEditorWrapper = ({
|
||||||
customColorToEdit,
|
customColorToEdit,
|
||||||
i18n,
|
i18n,
|
||||||
isInModal,
|
isGlobal,
|
||||||
onClose,
|
onClose,
|
||||||
onSave,
|
onSave,
|
||||||
}: CustomColorEditorWrapperPropsType): JSX.Element => {
|
}: CustomColorEditorWrapperPropsType): JSX.Element => {
|
||||||
|
@ -370,7 +403,7 @@ const CustomColorEditorWrapper = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isInModal) {
|
if (!isGlobal) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
hasXButton
|
hasXButton
|
||||||
|
|
|
@ -10,9 +10,11 @@ type PropsType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isChatColorEditorVisible: boolean;
|
isChatColorEditorVisible: boolean;
|
||||||
renderChatColorPicker: (actions: {
|
renderChatColorPicker: (actions: {
|
||||||
setAllConversationColors: (color: ConversationColorType) => unknown;
|
setGlobalDefaultConversationColor: (
|
||||||
|
color: ConversationColorType
|
||||||
|
) => unknown;
|
||||||
}) => JSX.Element;
|
}) => JSX.Element;
|
||||||
setAllConversationColors: (color: ConversationColorType) => unknown;
|
setGlobalDefaultConversationColor: (color: ConversationColorType) => unknown;
|
||||||
toggleChatColorEditor: () => unknown;
|
toggleChatColorEditor: () => unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ export const GlobalModalContainer = ({
|
||||||
i18n,
|
i18n,
|
||||||
isChatColorEditorVisible,
|
isChatColorEditorVisible,
|
||||||
renderChatColorPicker,
|
renderChatColorPicker,
|
||||||
setAllConversationColors,
|
setGlobalDefaultConversationColor,
|
||||||
toggleChatColorEditor,
|
toggleChatColorEditor,
|
||||||
}: PropsType): JSX.Element | null => {
|
}: PropsType): JSX.Element | null => {
|
||||||
if (isChatColorEditorVisible) {
|
if (isChatColorEditorVisible) {
|
||||||
|
@ -34,7 +36,7 @@ export const GlobalModalContainer = ({
|
||||||
title={i18n('ChatColorPicker__global-chat-color')}
|
title={i18n('ChatColorPicker__global-chat-color')}
|
||||||
>
|
>
|
||||||
{renderChatColorPicker({
|
{renderChatColorPicker({
|
||||||
setAllConversationColors,
|
setGlobalDefaultConversationColor,
|
||||||
})}
|
})}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
AvatarColorType,
|
AvatarColorType,
|
||||||
AvatarColors,
|
AvatarColors,
|
||||||
ConversationColorType,
|
ConversationColorType,
|
||||||
|
CustomColorType,
|
||||||
} from '../types/Colors';
|
} from '../types/Colors';
|
||||||
import { MessageModel } from './messages';
|
import { MessageModel } from './messages';
|
||||||
import { isMuted } from '../util/isMuted';
|
import { isMuted } from '../util/isMuted';
|
||||||
|
@ -1446,6 +1447,8 @@ export class ConversationModel extends window.Backbone
|
||||||
.filter((member): member is ConversationType => member !== null)
|
.filter((member): member is ConversationType => member !== null)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const { customColor, customColorId } = this.getCustomColorData();
|
||||||
|
|
||||||
// TODO: DESKTOP-720
|
// TODO: DESKTOP-720
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
const result: ConversationType = {
|
const result: ConversationType = {
|
||||||
|
@ -1469,8 +1472,8 @@ export class ConversationModel extends window.Backbone
|
||||||
unblurredAvatarPath: this.getAbsoluteUnblurredAvatarPath(),
|
unblurredAvatarPath: this.getAbsoluteUnblurredAvatarPath(),
|
||||||
color,
|
color,
|
||||||
conversationColor: this.getConversationColor(),
|
conversationColor: this.getConversationColor(),
|
||||||
customColor: this.get('customColor'),
|
customColor,
|
||||||
customColorId: this.get('customColorId'),
|
customColorId,
|
||||||
discoveredUnregisteredAt: this.get('discoveredUnregisteredAt'),
|
discoveredUnregisteredAt: this.get('discoveredUnregisteredAt'),
|
||||||
draftBodyRanges,
|
draftBodyRanges,
|
||||||
draftPreview,
|
draftPreview,
|
||||||
|
@ -4871,8 +4874,33 @@ export class ConversationModel extends window.Backbone
|
||||||
}
|
}
|
||||||
|
|
||||||
getConversationColor(): ConversationColorType {
|
getConversationColor(): ConversationColorType {
|
||||||
return (this.get('conversationColor') ||
|
const defaultConversationColor = window.storage.get(
|
||||||
'ultramarine') as ConversationColorType;
|
'defaultConversationColor'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (defaultConversationColor.customColorData) {
|
||||||
|
return 'custom';
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.get('conversationColor') || defaultConversationColor.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCustomColorData(): {
|
||||||
|
customColor?: CustomColorType;
|
||||||
|
customColorId?: string;
|
||||||
|
} {
|
||||||
|
const defaultConversationColor = window.storage.get(
|
||||||
|
'defaultConversationColor'
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
customColor:
|
||||||
|
this.get('customColor') ||
|
||||||
|
defaultConversationColor.customColorData?.value,
|
||||||
|
customColorId:
|
||||||
|
this.get('customColorId') ||
|
||||||
|
defaultConversationColor.customColorData?.id,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAvatarPath(): undefined | string {
|
private getAvatarPath(): undefined | string {
|
||||||
|
|
|
@ -1002,7 +1002,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
textPending: this.get('bodyPending'),
|
textPending: this.get('bodyPending'),
|
||||||
id: this.id,
|
id: this.id,
|
||||||
conversationColor: this.getConversationColor(),
|
conversationColor: this.getConversationColor(),
|
||||||
customColor: conversation?.get('customColor'),
|
customColor: conversation?.getCustomColorData()?.customColor,
|
||||||
conversationId: this.get('conversationId'),
|
conversationId: this.get('conversationId'),
|
||||||
isSticker: Boolean(sticker),
|
isSticker: Boolean(sticker),
|
||||||
direction: this.isIncoming() ? 'incoming' : 'outgoing',
|
direction: this.isIncoming() ? 'incoming' : 'outgoing',
|
||||||
|
|
21
ts/shims/reloadSelectedConversation.ts
Normal file
21
ts/shims/reloadSelectedConversation.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
export function reloadSelectedConversation(): void {
|
||||||
|
const { conversations } = window.reduxStore.getState();
|
||||||
|
const { selectedConversationId } = conversations;
|
||||||
|
if (!selectedConversationId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const conversation = window.ConversationController.get(
|
||||||
|
selectedConversationId
|
||||||
|
);
|
||||||
|
if (!conversation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conversation.cachedProps = undefined;
|
||||||
|
window.reduxActions.conversations.conversationChanged(
|
||||||
|
conversation.id,
|
||||||
|
conversation.format()
|
||||||
|
);
|
||||||
|
}
|
|
@ -379,7 +379,16 @@ type ColorsChangedActionType = {
|
||||||
};
|
};
|
||||||
type CustomColorRemovedActionType = {
|
type CustomColorRemovedActionType = {
|
||||||
type: typeof CUSTOM_COLOR_REMOVED;
|
type: typeof CUSTOM_COLOR_REMOVED;
|
||||||
payload: string;
|
payload: {
|
||||||
|
colorId: string;
|
||||||
|
defaultConversationColor: {
|
||||||
|
color: ConversationColorType;
|
||||||
|
customColorData?: {
|
||||||
|
id: string;
|
||||||
|
value: CustomColorType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
type SetPreJoinConversationActionType = {
|
type SetPreJoinConversationActionType = {
|
||||||
type: 'SET_PRE_JOIN_CONVERSATION';
|
type: 'SET_PRE_JOIN_CONVERSATION';
|
||||||
|
@ -697,7 +706,6 @@ export const actions = {
|
||||||
reviewMessageRequestNameCollision,
|
reviewMessageRequestNameCollision,
|
||||||
scrollToMessage,
|
scrollToMessage,
|
||||||
selectMessage,
|
selectMessage,
|
||||||
setAllConversationColors,
|
|
||||||
setComposeGroupAvatar,
|
setComposeGroupAvatar,
|
||||||
setComposeGroupName,
|
setComposeGroupName,
|
||||||
setComposeSearchTerm,
|
setComposeSearchTerm,
|
||||||
|
@ -741,9 +749,16 @@ function removeCustomColorOnConversations(
|
||||||
await window.Signal.Data.updateConversations(conversationsToUpdate);
|
await window.Signal.Data.updateConversations(conversationsToUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultConversationColor = window.storage.get(
|
||||||
|
'defaultConversationColor'
|
||||||
|
);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CUSTOM_COLOR_REMOVED,
|
type: CUSTOM_COLOR_REMOVED,
|
||||||
payload: colorId,
|
payload: {
|
||||||
|
colorId,
|
||||||
|
defaultConversationColor,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -769,44 +784,15 @@ function resetAllChatColors(): ThunkAction<
|
||||||
delete conversation.attributes.customColorId;
|
delete conversation.attributes.customColorId;
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
const defaultConversationColor = window.storage.get(
|
||||||
type: COLORS_CHANGED,
|
'defaultConversationColor'
|
||||||
payload: {
|
|
||||||
conversationColor: undefined,
|
|
||||||
customColorData: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAllConversationColors(
|
|
||||||
conversationColor: ConversationColorType,
|
|
||||||
customColorData?: {
|
|
||||||
id: string;
|
|
||||||
value: CustomColorType;
|
|
||||||
}
|
|
||||||
): ThunkAction<void, RootStateType, unknown, ColorsChangedActionType> {
|
|
||||||
return async dispatch => {
|
|
||||||
await window.Signal.Data.updateAllConversationColors(
|
|
||||||
conversationColor,
|
|
||||||
customColorData
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// We don't want to trigger a model change because we're updating redux
|
|
||||||
// here manually ourselves. Au revoir Backbone!
|
|
||||||
window.getConversations().forEach(conversation => {
|
|
||||||
Object.assign(conversation.attributes, {
|
|
||||||
conversationColor,
|
|
||||||
customColor: customColorData?.value,
|
|
||||||
customColorId: customColorData?.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: COLORS_CHANGED,
|
type: COLORS_CHANGED,
|
||||||
payload: {
|
payload: {
|
||||||
conversationColor,
|
conversationColor: defaultConversationColor.color,
|
||||||
customColorData,
|
customColorData: defaultConversationColor.customColorData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -2547,7 +2533,7 @@ export function reducer(
|
||||||
|
|
||||||
if (action.type === CUSTOM_COLOR_REMOVED) {
|
if (action.type === CUSTOM_COLOR_REMOVED) {
|
||||||
const { conversationLookup } = state;
|
const { conversationLookup } = state;
|
||||||
const colorId = action.payload;
|
const { colorId, defaultConversationColor } = action.payload;
|
||||||
|
|
||||||
const nextState = {
|
const nextState = {
|
||||||
...state,
|
...state,
|
||||||
|
@ -2560,11 +2546,12 @@ export function reducer(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changed = omit(existing, [
|
const changed = {
|
||||||
'conversationColor',
|
...existing,
|
||||||
'customColor',
|
conversationColor: defaultConversationColor.color,
|
||||||
'customColorId',
|
customColor: defaultConversationColor.customColorData?.value,
|
||||||
]);
|
customColorId: defaultConversationColor.customColorData?.id,
|
||||||
|
};
|
||||||
|
|
||||||
Object.assign(
|
Object.assign(
|
||||||
nextState,
|
nextState,
|
||||||
|
|
|
@ -7,7 +7,12 @@ import { ThunkAction } from 'redux-thunk';
|
||||||
import { StateType as RootStateType } from '../reducer';
|
import { StateType as RootStateType } from '../reducer';
|
||||||
import * as storageShim from '../../shims/storage';
|
import * as storageShim from '../../shims/storage';
|
||||||
import { useBoundActions } from '../../util/hooks';
|
import { useBoundActions } from '../../util/hooks';
|
||||||
import { CustomColorType } from '../../types/Colors';
|
import {
|
||||||
|
ConversationColors,
|
||||||
|
ConversationColorType,
|
||||||
|
CustomColorType,
|
||||||
|
} from '../../types/Colors';
|
||||||
|
import { reloadSelectedConversation } from '../../shims/reloadSelectedConversation';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
|
@ -15,6 +20,16 @@ export type ItemsStateType = {
|
||||||
readonly universalExpireTimer?: number;
|
readonly universalExpireTimer?: number;
|
||||||
|
|
||||||
readonly [key: string]: unknown;
|
readonly [key: string]: unknown;
|
||||||
|
|
||||||
|
// This property should always be set and this is ensured in background.ts
|
||||||
|
readonly defaultConversationColor?: {
|
||||||
|
color: ConversationColorType;
|
||||||
|
customColorData?: {
|
||||||
|
id: string;
|
||||||
|
value: CustomColorType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
readonly customColors?: {
|
readonly customColors?: {
|
||||||
readonly colors: Record<string, CustomColorType>;
|
readonly colors: Record<string, CustomColorType>;
|
||||||
readonly version: number;
|
readonly version: number;
|
||||||
|
@ -63,6 +78,8 @@ export const actions = {
|
||||||
addCustomColor,
|
addCustomColor,
|
||||||
editCustomColor,
|
editCustomColor,
|
||||||
removeCustomColor,
|
removeCustomColor,
|
||||||
|
resetDefaultChatColor,
|
||||||
|
setGlobalDefaultConversationColor,
|
||||||
onSetSkinTone,
|
onSetSkinTone,
|
||||||
putItem,
|
putItem,
|
||||||
putItemExternal,
|
putItemExternal,
|
||||||
|
@ -184,10 +201,48 @@ function removeCustomColor(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetDefaultChatColor(): ThunkAction<
|
||||||
|
void,
|
||||||
|
RootStateType,
|
||||||
|
unknown,
|
||||||
|
ItemPutAction
|
||||||
|
> {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(
|
||||||
|
putItem('defaultConversationColor', {
|
||||||
|
color: ConversationColors[0],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
reloadSelectedConversation();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGlobalDefaultConversationColor(
|
||||||
|
color: ConversationColorType,
|
||||||
|
customColorData?: {
|
||||||
|
id: string;
|
||||||
|
value: CustomColorType;
|
||||||
|
}
|
||||||
|
): ThunkAction<void, RootStateType, unknown, ItemPutAction> {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(
|
||||||
|
putItem('defaultConversationColor', {
|
||||||
|
color,
|
||||||
|
customColorData,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
reloadSelectedConversation();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
|
|
||||||
function getEmptyState(): ItemsStateType {
|
function getEmptyState(): ItemsStateType {
|
||||||
return {};
|
return {
|
||||||
|
defaultConversationColor: {
|
||||||
|
color: ConversationColors[0],
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reducer(
|
export function reducer(
|
||||||
|
|
|
@ -7,6 +7,11 @@ import { ITEM_NAME as UNIVERSAL_EXPIRE_TIMER_ITEM } from '../../util/universalEx
|
||||||
|
|
||||||
import { StateType } from '../reducer';
|
import { StateType } from '../reducer';
|
||||||
import { ItemsStateType } from '../ducks/items';
|
import { ItemsStateType } from '../ducks/items';
|
||||||
|
import {
|
||||||
|
ConversationColors,
|
||||||
|
ConversationColorType,
|
||||||
|
CustomColorType,
|
||||||
|
} from '../../types/Colors';
|
||||||
|
|
||||||
export const getItems = (state: StateType): ItemsStateType => state.items;
|
export const getItems = (state: StateType): ItemsStateType => state.items;
|
||||||
|
|
||||||
|
@ -25,3 +30,16 @@ export const getUniversalExpireTimer = createSelector(
|
||||||
getItems,
|
getItems,
|
||||||
(state: ItemsStateType): number => state[UNIVERSAL_EXPIRE_TIMER_ITEM] || 0
|
(state: ItemsStateType): number => state[UNIVERSAL_EXPIRE_TIMER_ITEM] || 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getDefaultConversationColor = createSelector(
|
||||||
|
getItems,
|
||||||
|
(
|
||||||
|
state: ItemsStateType
|
||||||
|
): {
|
||||||
|
color: ConversationColorType;
|
||||||
|
customColorData?: {
|
||||||
|
id: string;
|
||||||
|
value: CustomColorType;
|
||||||
|
};
|
||||||
|
} => state.defaultConversationColor ?? { color: ConversationColors[0] }
|
||||||
|
);
|
||||||
|
|
|
@ -13,13 +13,13 @@ import { StateType } from '../reducer';
|
||||||
import {
|
import {
|
||||||
getConversationSelector,
|
getConversationSelector,
|
||||||
getConversationsWithCustomColorSelector,
|
getConversationsWithCustomColorSelector,
|
||||||
getMe,
|
|
||||||
} from '../selectors/conversations';
|
} from '../selectors/conversations';
|
||||||
|
import { getDefaultConversationColor } from '../selectors/items';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
|
|
||||||
export type SmartChatColorPickerProps = {
|
export type SmartChatColorPickerProps = {
|
||||||
conversationId?: string;
|
conversationId?: string;
|
||||||
isInModal?: boolean;
|
isGlobal?: boolean;
|
||||||
onChatColorReset?: () => unknown;
|
onChatColorReset?: () => unknown;
|
||||||
onSelectColor: (
|
onSelectColor: (
|
||||||
color: ConversationColorType,
|
color: ConversationColorType,
|
||||||
|
@ -34,9 +34,14 @@ const mapStateToProps = (
|
||||||
state: StateType,
|
state: StateType,
|
||||||
props: SmartChatColorPickerProps
|
props: SmartChatColorPickerProps
|
||||||
): PropsDataType => {
|
): PropsDataType => {
|
||||||
const conversation = props.conversationId
|
const defaultConversationColor = getDefaultConversationColor(state);
|
||||||
|
const colorValues = props.conversationId
|
||||||
? getConversationSelector(state)(props.conversationId)
|
? getConversationSelector(state)(props.conversationId)
|
||||||
: getMe(state);
|
: {
|
||||||
|
conversationColor: defaultConversationColor.color,
|
||||||
|
customColorId: defaultConversationColor.customColorData?.id,
|
||||||
|
customColor: defaultConversationColor.customColorData?.value,
|
||||||
|
};
|
||||||
|
|
||||||
const { customColors } = state.items;
|
const { customColors } = state.items;
|
||||||
|
|
||||||
|
@ -47,10 +52,10 @@ const mapStateToProps = (
|
||||||
state
|
state
|
||||||
),
|
),
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
selectedColor: conversation.conversationColor,
|
selectedColor: colorValues.conversationColor,
|
||||||
selectedCustomColor: {
|
selectedCustomColor: {
|
||||||
id: conversation.customColorId,
|
id: colorValues.customColorId,
|
||||||
value: conversation.customColor,
|
value: colorValues.customColor,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,12 +11,15 @@ import { SmartChatColorPicker } from './ChatColorPicker';
|
||||||
import { ConversationColorType } from '../../types/Colors';
|
import { ConversationColorType } from '../../types/Colors';
|
||||||
|
|
||||||
function renderChatColorPicker({
|
function renderChatColorPicker({
|
||||||
setAllConversationColors,
|
setGlobalDefaultConversationColor,
|
||||||
}: {
|
}: {
|
||||||
setAllConversationColors: (color: ConversationColorType) => unknown;
|
setGlobalDefaultConversationColor: (color: ConversationColorType) => unknown;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<SmartChatColorPicker isInModal onSelectColor={setAllConversationColors} />
|
<SmartChatColorPicker
|
||||||
|
isGlobal
|
||||||
|
onSelectColor={setGlobalDefaultConversationColor}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ const {
|
||||||
openConversationInternal,
|
openConversationInternal,
|
||||||
repairNewestMessage,
|
repairNewestMessage,
|
||||||
repairOldestMessage,
|
repairOldestMessage,
|
||||||
setAllConversationColors,
|
|
||||||
setComposeGroupAvatar,
|
setComposeGroupAvatar,
|
||||||
setComposeGroupName,
|
setComposeGroupName,
|
||||||
setComposeSearchTerm,
|
setComposeSearchTerm,
|
||||||
|
@ -2041,49 +2040,10 @@ describe('both/state/ducks/conversations', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setAllConversationColors', async () => {
|
|
||||||
const dispatch = sinon.spy();
|
|
||||||
await setAllConversationColors('crimson')(dispatch, getState, null);
|
|
||||||
|
|
||||||
const [action] = dispatch.getCall(0).args;
|
|
||||||
const nextState = reducer(getState().conversations, action);
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatch);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationLookup.abc.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationLookup.def.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationLookup.ghi.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationLookup.jkl.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationsByUuid.abc.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationsByUuid.def.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationsByE164.ghi.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
nextState.conversationsByGroupId.jkl.conversationColor,
|
|
||||||
'crimson'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('resetAllChatColors', async () => {
|
it('resetAllChatColors', async () => {
|
||||||
|
window.storage.put('defaultConversationColor', {
|
||||||
|
color: 'crimson',
|
||||||
|
});
|
||||||
const dispatch = sinon.spy();
|
const dispatch = sinon.spy();
|
||||||
await resetAllChatColors()(dispatch, getState, null);
|
await resetAllChatColors()(dispatch, getState, null);
|
||||||
|
|
||||||
|
@ -2091,38 +2051,39 @@ describe('both/state/ducks/conversations', () => {
|
||||||
const nextState = reducer(getState().conversations, action);
|
const nextState = reducer(getState().conversations, action);
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatch);
|
sinon.assert.calledOnce(dispatch);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationLookup.abc.conversationColor,
|
nextState.conversationLookup.abc.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationLookup.def.conversationColor,
|
nextState.conversationLookup.def.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationLookup.ghi.conversationColor,
|
nextState.conversationLookup.ghi.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationLookup.jkl.conversationColor,
|
nextState.conversationLookup.jkl.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationsByUuid.abc.conversationColor,
|
nextState.conversationsByUuid.abc.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationsByUuid.def.conversationColor,
|
nextState.conversationsByUuid.def.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationsByE164.ghi.conversationColor,
|
nextState.conversationsByE164.ghi.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
assert.isUndefined(
|
assert.equal(
|
||||||
nextState.conversationsByGroupId.jkl.conversationColor,
|
nextState.conversationsByGroupId.jkl.conversationColor,
|
||||||
'crimson'
|
'crimson'
|
||||||
);
|
);
|
||||||
|
window.storage.remove('defaultConversationColor');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue