feat: add theme switcher +accent color from settings

not all of the logic is linked and not all of the colors are there as this is part of theming
This commit is contained in:
Audric Ackermann 2022-08-26 10:40:31 +10:00
parent f137cad2a0
commit 1e202fcdab
14 changed files with 439 additions and 67 deletions

View File

@ -140,6 +140,19 @@
"typingIndicatorsSettingDescription": "See and share typing indicators in one-to-one chats.",
"typingIndicatorsSettingTitle": "Typing Indicators",
"zoomFactorSettingTitle": "Zoom Factor",
"themesSettingTitle": "Themes",
"primaryColor": "Primary Color",
"primaryColorGreen": "Primary color green",
"primaryColorBlue": "Primary color blue",
"primaryColorYellow": "Primary color yellow",
"primaryColorPink": "Primary color pink",
"primaryColorPurple": "Primary color purple",
"primaryColorOrange": "Primary color orange",
"primaryColorRed": "Primary color red",
"classicDarkThemeTitle": "Classic Dark",
"classicLightThemeTitle": "Classic Light",
"oceanDarkThemeTitle": "Ocean Dark",
"oceanLightThemeTitle": "Ocean Light",
"pruneSettingTitle": "Trim Communities",
"pruneSettingDescription": "Delete messages from Communities older than 6 months, and where there are over 2,000 messages.",
"pruningOpengroupDialogTitle": "Community pruning",

View File

@ -1,5 +1,5 @@
import React, { ChangeEvent } from 'react';
import styled, { CSSProperties } from 'styled-components';
import styled from 'styled-components';
import { Flex } from '../basic/Flex';
// tslint:disable: react-unused-props-and-state
@ -11,37 +11,43 @@ type Props = {
onClick?: (value: string) => void;
};
const StyledInput = styled.input`
const StyledInput = styled.input<{
filledSize: number;
outlineOffset: number;
selectedColor: string;
}>`
opacity: 0;
position: absolute;
cursor: pointer;
width: calc(var(--filled-size) + var(--outline-offset));
height: calc(var(--filled-size) + var(--outline-offset));
width: ${props => props.filledSize + props.outlineOffset}px;
height: ${props => props.filledSize + props.outlineOffset}px;
:checked + label:before,
:hover + label:before {
background: var(--color-accent);
background: ${props => props.selectedColor};
}
`;
const StyledLabel = styled.label`
const StyledLabel = styled.label<{
selectedColor: string;
filledSize: number;
outlineOffset: number;
}>`
cursor: pointer;
:before {
content: '';
display: inline-block;
margin-inline-end: var(--filled-size);
border-radius: 100%;
transition: var(--default-duration);
padding: calc(var(--filled-size) / 2);
outline-offset: 2px;
padding: ${props => props.filledSize}px;
outline: var(--color-text) solid 1px;
border: none;
margin-top: var(--filled-size);
outline-offset: ${props => props.outlineOffset}px;
:hover {
background: var(--color-accent);
background: ${props => props.selectedColor};
}
}
`;
@ -53,20 +59,16 @@ export const SessionRadio = (props: Props) => {
if (onClick) {
// let something else catch the event if our click handler is not set
e.stopPropagation();
onClick?.(value);
onClick(value);
}
}
const selectedColor = 'var(--color-accent)';
const filledSize = 15 / 2;
const outlineOffset = 2;
return (
<Flex
container={true}
padding="0 0 5px"
style={
{
'--filled-size': '15px',
} as CSSProperties
}
>
<Flex container={true} padding="0 0 0 var(--margins-lg)">
<StyledInput
type="radio"
name={inputName || ''}
@ -74,11 +76,85 @@ export const SessionRadio = (props: Props) => {
aria-checked={active}
checked={active}
onChange={clickHandler}
filledSize={filledSize}
outlineOffset={outlineOffset}
selectedColor={selectedColor}
/>
<StyledLabel role="button" onClick={clickHandler}>
<StyledLabel
role="button"
onClick={clickHandler}
selectedColor={selectedColor}
filledSize={filledSize}
outlineOffset={outlineOffset}
>
{label}
</StyledLabel>
</Flex>
);
};
const StyledInputOutlineSelected = styled(StyledInput)`
label:before,
label:before {
outline: none;
}
:checked + label:before {
outline: var(--color-text) solid 1px;
}
`;
const StyledLabelOutlineSelected = styled(StyledLabel)<{ selectedColor: string }>`
:before {
background: ${props => props.selectedColor};
outline: #0000 solid 1px;
}
`;
/**
* Keeping this component here so we can reuse the `StyledInput` and `StyledLabel` defined locally rather than exporting them
*/
export const SessionRadioPrimaryColors = (props: {
value: string;
active: boolean;
inputName?: string;
onClick: (value: string) => void;
ariaLabel: string;
color: string; // by default, we use the theme accent color but for the settings screen we need to be able to force it
}) => {
const { inputName, value, active, onClick, color, ariaLabel } = props;
function clickHandler(e: ChangeEvent<any>) {
e.stopPropagation();
onClick(value);
}
const filledSize = 31 / 2;
const outlineOffset = 5;
return (
<Flex container={true} padding="0 0 5px 0">
<StyledInputOutlineSelected
type="radio"
name={inputName || ''}
value={value}
aria-checked={active}
checked={active}
onChange={clickHandler}
filledSize={filledSize}
outlineOffset={outlineOffset}
selectedColor={color}
aria-label={ariaLabel}
/>
<StyledLabelOutlineSelected
role="button"
onClick={clickHandler}
selectedColor={color}
filledSize={filledSize}
outlineOffset={outlineOffset}
>
{''}
</StyledLabelOutlineSelected>
</Flex>
);
};

View File

@ -18,7 +18,6 @@ import {
getOurPrimaryConversation,
getUnreadMessageCount,
} from '../../state/selectors/conversations';
import { applyTheme } from '../../state/ducks/theme';
import { getFocusedSection } from '../../state/selectors/section';
import { clearSearch } from '../../state/ducks/search';
import { resetOverlayMode, SectionType, showLeftPaneSection } from '../../state/ducks/section';
@ -39,7 +38,6 @@ import { debounce, isEmpty, isString } from 'lodash';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
import { ActionPanelOnionStatusLight } from '../dialog/OnionStatusPathDialog';
import { switchHtmlToDarkTheme, switchHtmlToLightTheme } from '../../state/ducks/SessionTheme';
import { loadDefaultRooms } from '../../session/apis/open_group_api/opengroupV2/ApiUtil';
import { getOpenGroupManager } from '../../session/apis/open_group_api/opengroupV2/OpenGroupManagerV2';
import { getSwarmPollingInstance } from '../../session/apis/snode_api';
@ -57,6 +55,7 @@ import { UserUtils } from '../../session/utils';
import { Storage } from '../../util/storage';
import { SettingsKey } from '../../data/settings-key';
import { getLatestReleaseFromFileServer } from '../../session/apis/file_server_api/FileServerApi';
import { switchThemeTo } from '../../session/utils/Theme';
const Section = (props: { type: SectionType }) => {
const ourNumber = useSelector(getOurNumber);
@ -67,22 +66,15 @@ const Section = (props: { type: SectionType }) => {
const focusedSection = useSelector(getFocusedSection);
const isSelected = focusedSection === props.type;
const handleClick = () => {
const handleClick = async () => {
/* tslint:disable:no-void-expression */
if (type === SectionType.Profile) {
dispatch(editProfileModal({}));
} else if (type === SectionType.Moon) {
const themeFromSettings = window.Events.getThemeSetting();
const updatedTheme = themeFromSettings === 'dark' ? 'light' : 'dark';
window.setTheme(updatedTheme);
if (updatedTheme === 'dark') {
switchHtmlToDarkTheme();
} else {
switchHtmlToLightTheme();
}
const currentTheme = window.Events.getThemeSetting();
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
const newThemeObject = updatedTheme === 'dark' ? 'dark' : 'light';
dispatch(applyTheme(newThemeObject));
await switchThemeTo(newTheme, dispatch);
} else if (type === SectionType.PathIndicator) {
// Show Path Indicator Modal
dispatch(onionPathModal({}));
@ -163,16 +155,9 @@ const cleanUpMediasInterval = DURATION.MINUTES * 60;
// * if there is a version on the fileserver more recent than our current, we fetch github to get the UpdateInfos and trigger an update as usual (asking user via dialog)
const fetchReleaseFromFileServerInterval = 1000 * 60; // try to fetch the latest release from the fileserver every minute
const setupTheme = () => {
const setupTheme = async () => {
const theme = window.Events.getThemeSetting();
window.setTheme(theme);
if (theme === 'dark') {
switchHtmlToDarkTheme();
} else {
switchHtmlToLightTheme();
}
const newThemeObject = theme === 'dark' ? 'dark' : 'light';
window?.inboxStore?.dispatch(applyTheme(newThemeObject));
await switchThemeTo(theme, window?.inboxStore?.dispatch || null);
};
// Do this only if we created a new Session ID, or if we already received the initial configuration message

View File

@ -15,7 +15,7 @@ type ButtonSettingsProps = {
onClick: () => void;
};
const StyledDescription = styled.div`
export const StyledDescriptionSettingsItem = styled.div`
font-family: var(--font-default);
font-size: var(--font-size-sm);
font-weight: 400;
@ -23,7 +23,7 @@ const StyledDescription = styled.div`
color: var(--color-text-subtle);
`;
const StyledTitle = styled.div`
export const StyledTitleSettingsItem = styled.div`
line-height: 1.7;
font-size: var(--font-size-lg);
font-weight: bold;
@ -33,7 +33,7 @@ const StyledInfo = styled.div`
padding-inline-end: var(--margins-lg);
`;
const StyledDescriptionContainer = styled(StyledDescription)`
const StyledDescriptionContainer = styled(StyledDescriptionSettingsItem)`
display: flex;
align-items: center;
`;
@ -69,9 +69,11 @@ export const SettingsTitleAndDescription = (props: {
const { description, childrenDescription, title } = props;
return (
<StyledInfo>
<StyledTitle>{title}</StyledTitle>
<StyledTitleSettingsItem>{title}</StyledTitleSettingsItem>
<StyledDescriptionContainer>
{description && <StyledDescription>{description}</StyledDescription>}
{description && (
<StyledDescriptionSettingsItem>{description}</StyledDescriptionSettingsItem>
)}
<>{childrenDescription}</>
</StyledDescriptionContainer>
</StyledInfo>

View File

@ -0,0 +1,203 @@
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { switchThemeTo } from '../../session/utils/Theme';
import {
darkColorReceivedMessageBg,
darkColorSentMessageBg,
getPrimaryColors,
lightColorReceivedMessageBg,
lightColorSentMessageBg,
OceanBlueDark,
OceanBlueLight,
PrimaryColorIds,
} from '../../state/ducks/SessionTheme';
import { ThemeStateType } from '../../state/ducks/theme';
import { getTheme } from '../../state/selectors/theme';
import { SessionRadio, SessionRadioPrimaryColors } from '../basic/SessionRadio';
import { SpacerLG, SpacerMD } from '../basic/Text';
import { StyledDescriptionSettingsItem, StyledTitleSettingsItem } from './SessionSettingListItem';
// tslint:disable: use-simple-attributes
const StyledSwitcherContainer = styled.div`
font-size: var(--font-size-md);
padding: var(--margins-lg);
background: var(--color-cell-background);
`;
const ThemeContainer = styled.button`
background: var(--color-conversation-list);
border: 1px solid var(--color-clickable-hovered);
border-radius: 8px;
padding: var(--margins-sm);
display: flex;
align-items: center;
width: 285px;
height: 90px;
transition: var(--default-duration);
:hover {
background: var(--color-clickable-hovered);
}
`;
const ThemesContainer = styled.div`
display: flex;
flex-wrap: wrap;
gap: var(--margins-lg);
`;
type ThemeType = {
id: ThemeStateType;
title: string;
style: StyleSessionSwitcher;
};
type StyleSessionSwitcher = {
background: string;
border: string;
receivedBg: string;
sentBg: string;
};
const StyledPreview = styled.svg`
max-height: 100%;
`;
const ThemePreview = (props: { style: StyleSessionSwitcher }) => {
return (
<StyledPreview xmlSpace="preserve" viewBox="0 0 80 72" fill={props.style.background}>
<path
stroke={props.style.border}
d="M7.5.9h64.6c3.6 0 6.5 2.9 6.5 6.5v56.9c0 3.6-2.9 6.5-6.5 6.5H7.5c-3.6 0-6.5-2.9-6.5-6.5V7.4C1 3.9 3.9.9 7.5.9z"
/>
<path
fill={props.style.receivedBg}
d="M8.7 27.9c0-3.2 2.6-5.7 5.7-5.7h30.4c3.2 0 5.7 2.6 5.7 5.7 0 3.2-2.6 5.7-5.7 5.7H14.4c-3.1.1-5.7-2.5-5.7-5.7z"
/>
<path
fill={props.style.sentBg}
d="M32.6 42.2c0-3.2 2.6-5.7 5.7-5.7h27c3.2 0 5.7 2.6 5.7 5.7 0 3.2-2.6 5.7-5.7 5.7h-27c-3.1 0-5.7-2.5-5.7-5.7z"
/>
</StyledPreview>
);
};
const Themes = (props: { selectedAccent?: PrimaryColorIds }) => {
const { selectedAccent } = props;
// I am not too sure if we want to override the accent color on the Theme switcher of not.
// If we do, we also need a way to rollback to the default, I guess?
const overridenAccent = selectedAccent
? getPrimaryColors().find(e => {
return e.id === selectedAccent;
})?.color
: undefined;
const themes: Array<ThemeType> = [
{
id: 'dark',
title: window.i18n('classicDarkThemeTitle'),
style: {
background: '#000000',
border: '#414141',
receivedBg: darkColorReceivedMessageBg,
sentBg: overridenAccent || darkColorSentMessageBg,
},
},
{
id: 'light',
title: window.i18n('classicLightThemeTitle'),
style: {
background: '#ffffff',
border: '#414141',
receivedBg: lightColorReceivedMessageBg,
sentBg: overridenAccent || lightColorSentMessageBg,
},
},
{
id: 'ocean-dark',
title: window.i18n('oceanDarkThemeTitle'),
style: {
background: OceanBlueDark.background,
border: OceanBlueDark.border,
receivedBg: OceanBlueDark.received,
sentBg: overridenAccent || OceanBlueDark.sent,
},
},
{
id: 'ocean-light',
title: window.i18n('oceanLightThemeTitle'),
style: {
background: OceanBlueLight.background,
border: OceanBlueLight.border,
receivedBg: OceanBlueLight.received,
sentBg: overridenAccent || OceanBlueLight.sent,
},
},
];
const selectedTheme = useSelector(getTheme);
const dispatch = useDispatch();
return (
<>
{themes.map(theme => {
function onSelectTheme() {
void switchThemeTo(theme.id, dispatch);
}
return (
<ThemeContainer key={theme.id} onClick={onSelectTheme}>
<ThemePreview style={theme.style} />
<SpacerLG />
<StyledTitleSettingsItem>{theme.title}</StyledTitleSettingsItem>
<SessionRadio
active={selectedTheme === theme.id}
label={''}
value={theme.id}
inputName={'theme-switcher'}
/>
</ThemeContainer>
);
})}
</>
);
};
export const SettingsThemeSwitcher = () => {
//FIXME store that value somewhere in the theme object
const [selectedAccent, setSelectedAccent] = useState<PrimaryColorIds | undefined>(undefined);
return (
<StyledSwitcherContainer>
<StyledTitleSettingsItem>{window.i18n('themesSettingTitle')}</StyledTitleSettingsItem>
<ThemesContainer>
<Themes selectedAccent={selectedAccent} />
</ThemesContainer>
<SpacerMD />
<StyledDescriptionSettingsItem>{window.i18n('primaryColor')}</StyledDescriptionSettingsItem>
<SpacerMD />
<ThemesContainer style={{ marginInlineStart: 'var(--margins-xs)' }}>
{getPrimaryColors().map(item => {
return (
<SessionRadioPrimaryColors
key={item.id}
active={item.id === selectedAccent}
value={item.id}
inputName="primary-colors"
ariaLabel={item.ariaLabel}
color={item.color}
onClick={() => {
setSelectedAccent(item.id);
}}
/>
);
})}
</ThemesContainer>
</StyledSwitcherContainer>
);
};

View File

@ -5,6 +5,7 @@ import { SettingsKey } from '../../../data/settings-key';
import { isHideMenuBarSupported } from '../../../types/Settings';
import { SessionToggleWithDescription } from '../SessionSettingListItem';
import { SettingsThemeSwitcher } from '../SettingsThemeSwitcher';
import { ZoomingSessionSlider } from '../ZoomingSessionSlider';
export const SettingsCategoryAppearance = (props: { hasPassword: boolean | null }) => {
@ -18,7 +19,7 @@ export const SettingsCategoryAppearance = (props: { hasPassword: boolean | null
return (
<>
{/* TODO: add theme switching here */}
<SettingsThemeSwitcher />
<ZoomingSessionSlider />
{isHideMenuBarSupported() && (
<SessionToggleWithDescription

View File

@ -169,7 +169,7 @@ Storage.onready(async () => {
const themeSetting = window.Events.getThemeSetting();
const newThemeSetting = mapOldThemeToNew(themeSetting);
window.Events.setThemeSetting(newThemeSetting);
await window.Events.setThemeSetting(newThemeSetting);
try {
initialiseEmojiData(nativeEmojiData);
@ -281,8 +281,8 @@ async function start() {
// tslint:disable-next-line: restrict-plus-operands
const launchCount = !prevLaunchCount ? 1 : prevLaunchCount + 1;
window.setTheme = newTheme => {
window.Events.setThemeSetting(newTheme);
window.setTheme = async newTheme => {
await window.Events.setThemeSetting(newTheme);
};
window.toggleMenuBar = () => {

View File

@ -0,0 +1,28 @@
import { Dispatch } from 'redux';
import { switchHtmlToDarkTheme, switchHtmlToLightTheme } from '../../state/ducks/SessionTheme';
import { applyTheme, ThemeStateType } from '../../state/ducks/theme';
export async function switchThemeTo(theme: ThemeStateType, dispatch: Dispatch | null) {
await window.setTheme(theme);
// for now, do not switch to ocean light nor dark theme as the SessionTheme associated with them is not complete
let newTheme: ThemeStateType | null = null;
switch (theme) {
case 'dark':
switchHtmlToDarkTheme();
newTheme = 'dark';
break;
case 'light':
switchHtmlToLightTheme();
newTheme = 'light';
break;
default:
window.log.warn('Unsupported theme: ', theme);
}
if (newTheme) {
dispatch?.(applyTheme(newTheme));
}
}

View File

@ -24,7 +24,7 @@ const darkColorTextSubtle = `${white}99`;
const darkColorTextAccent = accentDarkTheme;
const darkColorSessionShadow = `0 0 4px 0 ${white}33`;
const darkColorComposeViewBg = '#232323';
const darkColorSentMessageBg = accentDarkTheme;
export const darkColorSentMessageBg = accentDarkTheme;
const darkColorClickableHovered = '#414347';
const darkColorSessionBorder = `1px solid ${borderDarkThemeColor}`;
const darkColorSessionBorderColor = borderDarkThemeColor;
@ -33,7 +33,7 @@ const darkColorPillDivider = '#353535';
const darkColorLastSeenIndicator = accentDarkTheme;
const darkColorQuoteBottomBarBg = '#404040';
const darkColorCellBackground = '#1b1b1b';
const darkColorReceivedMessageBg = '#2d2d2d';
export const darkColorReceivedMessageBg = '#2d2d2d';
const darkColorReceivedMessageText = white;
const darkColorPillDividerText = '#a0a0a0';
@ -170,7 +170,7 @@ const lightColorTextSubtle = `${black}99`;
const lightColorTextAccent = accentLightTheme;
const lightColorSessionShadow = `0 0 4px 0 ${black}5E`;
const lightColorComposeViewBg = '#efefef';
const lightColorSentMessageBg = accentLightTheme;
export const lightColorSentMessageBg = accentLightTheme;
const lightColorClickableHovered = '#dfdfdf';
const lightColorSessionBorderColor = borderLightThemeColor;
const lightColorSessionBorder = `1px solid ${lightColorSessionBorderColor}`;
@ -179,7 +179,7 @@ const lightColorPillDivider = `${black}1A`;
const lightColorLastSeenIndicator = black;
const lightColorQuoteBottomBarBg = '#f0f0f0';
const lightColorCellBackground = '#f9f9f9';
const lightColorReceivedMessageBg = '#f5f5f5';
export const lightColorReceivedMessageBg = '#f5f5f5';
const lightColorReceivedMessageText = black;
const lightColorPillDividerText = '#555555';
@ -400,3 +400,54 @@ export const SessionTheme = ({ children }: { children: any }) => (
{children}
</>
);
/**
* Just putting those new theme values used in the settings to avoid having conflicts for now.
*
*/
type SettingsThemeSwitcherColor = {
background: string;
border: string;
sent: string;
received: string;
};
export const OceanBlueDark: SettingsThemeSwitcherColor = {
background: '#242735',
border: '#3D4A5E',
sent: '#57C9FA',
received: '#3D4A5D',
};
export const OceanBlueLight: SettingsThemeSwitcherColor = {
background: '#ECFAFB',
border: '#5CAACC',
sent: '#57C9FA',
received: '#B3EDF2',
};
export type PrimaryColorIds =
| 'green'
| 'blue'
| 'yellow'
| 'pink'
| 'purple'
| 'orange'
| 'red'
| 'blue'
| 'blue'
| 'blue';
type PrimaryColorType = { id: PrimaryColorIds; ariaLabel: string; color: string };
export const getPrimaryColors = (): Array<PrimaryColorType> => {
return [
{ id: 'green', ariaLabel: window.i18n('primaryColorGreen'), color: '#31F196' },
{ id: 'blue', ariaLabel: window.i18n('primaryColorBlue'), color: '#57C9FA' },
{ id: 'yellow', ariaLabel: window.i18n('primaryColorYellow'), color: '#FAD657' },
{ id: 'pink', ariaLabel: window.i18n('primaryColorPink'), color: '#FF95EF' },
{ id: 'purple', ariaLabel: window.i18n('primaryColorPurple'), color: '#C993FF' },
{ id: 'orange', ariaLabel: window.i18n('primaryColorOrange'), color: '#FCB159' },
{ id: 'red', ariaLabel: window.i18n('primaryColorRed'), color: '#FF9C8E' },
];
};

View File

@ -91,8 +91,8 @@ export const actions = {
};
export const initialSectionState: SectionStateType = {
focusedSection: SectionType.Message,
focusedSettingsSection: undefined,
focusedSection: SectionType.Settings,
focusedSettingsSection: SessionSettingCategory.Appearance,
isAppFocused: false,
overlayMode: undefined,
};

View File

@ -1,6 +1,7 @@
export const APPLY_THEME = 'APPLY_THEME';
export type ThemeStateType = 'light' | 'dark';
export type ThemeStateType = 'light' | 'dark' | 'ocean-light' | 'ocean-dark';
export const applyTheme = (theme: ThemeStateType) => {
return {
type: APPLY_THEME,

View File

@ -51,7 +51,6 @@ export const getConversationsCount = createSelector(getConversationLookup, (stat
return Object.values(state).length;
});
export const getSelectedConversationKey = createSelector(
getConversations,
(state: ConversationsStateType): string | undefined => {

View File

@ -1,5 +1,6 @@
export type LocalizerKeys =
| 'removePassword'
| 'classicDarkThemeTitle'
| 'userUnbanFailed'
| 'changePassword'
| 'saved'
@ -102,6 +103,7 @@ export type LocalizerKeys =
| 'linkDevice'
| 'callMissedNotApproved'
| 'invalidPubkeyFormat'
| 'primaryColorYellow'
| 'disappearingMessagesDisabled'
| 'spellCheckDescription'
| 'clearDataSettingsTitle'
@ -110,6 +112,7 @@ export type LocalizerKeys =
| 'timerOption_30_minutes_abbreviated'
| 'pruneSettingDescription'
| 'voiceMessage'
| 'primaryColorPink'
| 'changePasswordTitle'
| 'copyMessage'
| 'messageDeletionForbidden'
@ -141,7 +144,7 @@ export type LocalizerKeys =
| 'contextMenuNoSuggestions'
| 'recoveryPhraseRevealButtonText'
| 'banUser'
| 'answeredACall'
| 'primaryColorBlue'
| 'sendMessage'
| 'readableListCounterSingular'
| 'recoveryPhraseRevealMessage'
@ -234,6 +237,7 @@ export type LocalizerKeys =
| 'failedToAddAsModerator'
| 'disabledDisappearingMessages'
| 'cannotUpdate'
| 'primaryColor'
| 'device'
| 'replyToMessage'
| 'messageDeletedPlaceholder'
@ -267,8 +271,11 @@ export type LocalizerKeys =
| 'resend'
| 'copiedToClipboard'
| 'closedGroupInviteSuccessTitlePlural'
| 'autoUpdateDownloadButtonLabel'
| 'groupMembers'
| 'primaryColorOrange'
| 'dialogClearAllDataDeletionQuestion'
| 'oceanDarkThemeTitle'
| 'unableToLoadAttachment'
| 'cameraPermissionNeededTitle'
| 'editMenuRedo'
@ -279,6 +286,7 @@ export type LocalizerKeys =
| 'newMessage'
| 'windowMenuClose'
| 'mainMenuFile'
| 'primaryColorPurple'
| 'callMissed'
| 'getStarted'
| 'unblockUser'
@ -302,7 +310,8 @@ export type LocalizerKeys =
| 'deleteConversationConfirmation'
| 'timerOption_6_hours_abbreviated'
| 'timerOption_1_week_abbreviated'
| 'timerSetTo'
| 'removePasswordTitle'
| 'unblockGroupToSend'
| 'enable'
| 'notificationSubtitle'
| 'youChangedTheTimer'
@ -313,6 +322,7 @@ export type LocalizerKeys =
| 'notificationForConvo'
| 'noNameOrMessage'
| 'pinConversationLimitTitle'
| 'classicLightThemeTitle'
| 'noSearchResults'
| 'changeNickname'
| 'userUnbanned'
@ -360,7 +370,7 @@ export type LocalizerKeys =
| 'failedResolveOns'
| 'showDebugLog'
| 'declineRequestMessage'
| 'autoUpdateDownloadButtonLabel'
| 'primaryColorGreen'
| 'dialogClearAllDataDeletionFailedTitleQuestion'
| 'autoUpdateDownloadInstructions'
| 'dialogClearAllDataDeletionFailedTitle'
@ -388,6 +398,8 @@ export type LocalizerKeys =
| 'deleteForEveryone'
| 'createSessionID'
| 'multipleLeftTheGroup'
| 'answeredACall'
| 'oceanLightThemeTitle'
| 'enterSessionIDOrONSName'
| 'quoteThumbnailAlt'
| 'timerOption_1_week'
@ -440,13 +452,14 @@ export type LocalizerKeys =
| 'settingsHeader'
| 'autoUpdateNewVersionMessage'
| 'oneNonImageAtATimeToast'
| 'removePasswordTitle'
| 'timerSetTo'
| 'iAmSure'
| 'primaryColorRed'
| 'selectMessage'
| 'enterAnOpenGroupURL'
| 'delete'
| 'changePasswordInvalid'
| 'unblockGroupToSend'
| 'themesSettingTitle'
| 'timerOption_6_hours'
| 'confirmPassword'
| 'downloadAttachment'

2
ts/window.d.ts vendored
View File

@ -56,7 +56,7 @@ declare global {
getCallMediaPermissions: () => boolean;
toggleMenuBar: () => void;
toggleSpellCheck: any;
setTheme: (newTheme: string) => any;
setTheme: (newTheme: string) => Promise<void>;
isDev?: () => boolean;
userConfig: any;
versionInfo: any;