add theme logic to switch between dark/light theme based on redux store

This commit is contained in:
Audric Ackermann 2020-10-15 10:07:38 +11:00
parent 6c7f1598b7
commit 1a379d2466
No known key found for this signature in database
GPG key ID: 999F434D76324AD4
13 changed files with 284 additions and 40 deletions

View file

@ -817,16 +817,12 @@
// Get memberlist. This function is not accurate >>
// window.getMemberList = window.lokiPublicChatAPI.getListOfMembers();
window.toggleTheme = () => {
const theme = window.Events.getThemeSetting();
const updatedTheme = theme === 'dark' ? 'light' : 'dark';
window.setTheme = newTheme => {
$(document.body)
.removeClass('dark-theme')
.removeClass('light-theme')
.addClass(`${updatedTheme}-theme`);
window.Events.setThemeSetting(updatedTheme);
.addClass(`${newTheme}-theme`);
window.Events.setThemeSetting(newTheme);
};
window.toggleMenuBar = () => {

View file

@ -101,8 +101,6 @@ export class LeftPane extends React.Component<Props, State> {
return this.renderContactSection();
case SectionType.Settings:
return this.renderSettingSection();
case SectionType.Moon:
return window.toggleTheme();
default:
return undefined;
}

View file

@ -1,8 +1,11 @@
import React from 'react';
import { connect, useDispatch } from 'react-redux';
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
import { Avatar } from '../Avatar';
import { PropsData as ConversationListItemPropsType } from '../ConversationListItem';
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { APPLY_THEME } from '../../state/ducks/theme';
import { darkTheme, lightTheme } from '../../state/ducks/SessionTheme';
export enum SectionType {
Profile,
@ -22,9 +25,10 @@ interface Props {
selectedSection: SectionType;
conversations: Array<ConversationListItemPropsType> | undefined;
unreadMessageCount: number;
dispatch?: any;
}
export class ActionsPanel extends React.Component<Props, State> {
class ActionsPanelPrivate extends React.Component<Props, State> {
private ourConversation: any;
constructor(props: Props) {
super(props);
@ -130,7 +134,16 @@ export class ActionsPanel extends React.Component<Props, State> {
if (type === SectionType.Profile) {
this.editProfileHandle();
} else if (type === SectionType.Moon) {
window.toggleTheme();
const theme = window.Events.getThemeSetting();
const updatedTheme = theme === 'dark' ? 'light' : 'dark';
window.setTheme(updatedTheme);
const newThemeObject =
updatedTheme === 'dark' ? darkTheme : lightTheme;
this.props.dispatch({
type: APPLY_THEME,
payload: newThemeObject,
});
} else {
onSelect(type);
}
@ -239,3 +252,5 @@ export class ActionsPanel extends React.Component<Props, State> {
this.props.onSectionSelected(section);
};
}
export const ActionsPanel = connect()(ActionsPanelPrivate);

View file

@ -21,6 +21,8 @@ import { UserUtil } from '../../../util';
import { MultiDeviceProtocol } from '../../../session/protocols';
import { ConversationHeaderWithDetails } from '../../conversation/ConversationHeader';
import { SessionRightPanelWithDetails } from './SessionRightPanel';
import { Theme } from '../../../state/ducks/SessionTheme';
import { DefaultTheme } from 'styled-components';
interface State {
conversationKey: string;
@ -54,9 +56,17 @@ interface State {
// dropZoneFiles?: FileList
dropZoneFiles: any;
// quoted message
quotedMessageProps?: ReplyingToMessageProps;
}
export class SessionConversation extends React.Component<any, State> {
interface Props {
conversations: any;
theme: DefaultTheme;
}
export class SessionConversation extends React.Component<Props, State> {
private readonly messagesEndRef: React.RefObject<HTMLDivElement>;
private readonly messageContainerRef: React.RefObject<HTMLDivElement>;
@ -210,7 +220,7 @@ export class SessionConversation extends React.Component<any, State> {
const showMessageDetails = this.state.infoViewState === 'messageDetails';
return (
<>
<Theme theme={this.props.theme}>
<div className="conversation-header">{this.renderHeader()}</div>
{/* <SessionProgress
@ -272,6 +282,10 @@ export class SessionConversation extends React.Component<any, State> {
onMessageFailure={this.onMessageFailure}
onLoadVoiceNoteView={this.onLoadVoiceNoteView}
onExitVoiceNoteView={this.onExitVoiceNoteView}
quotedMessageProps={quotedMessageProps}
removeQuotedMessage={() => {
this.replyToMessage(undefined);
}}
/>
)}
</div>
@ -286,7 +300,7 @@ export class SessionConversation extends React.Component<any, State> {
<SessionRightPanelWithDetails {...groupSettingsProps} />
</div>
)}
</>
</Theme>
);
}

View file

@ -342,19 +342,6 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
const { Settings } = window.Signal.Types;
return [
{
id: 'theme-setting',
title: window.i18n('themeToggleTitle'),
description: window.i18n('themeToggleDescription'),
hidden: true,
comparisonValue: 'light',
type: SessionSettingType.Toggle,
category: SessionSettingCategory.Appearance,
setFn: window.toggleTheme,
content: undefined,
onClick: undefined,
confirmationDialogParams: undefined,
},
{
id: 'hide-menu-bar',
title: window.i18n('hideMenuBarTitle'),

View file

@ -41,20 +41,17 @@ export const UI = {
// COMMON
WHITE: '#FFFFFF',
WHITE_PALE: '#AFAFAF',
LIGHT_GREY: '#A0A0A0',
DARK_GREY: '#353535',
BLACK: '#000000',
GREEN: '#00F782',
// SEMANTIC COLORS
INFO: '#3F3F3F',
SUCCESS: '#35D388',
ERROR: '#EDD422',
WARNING: '#A0A0A0',
WARNING_ALT: '#FF9D00',
DANGER: '#FF453A',
DANGER_ALT: '#FF4538',
PRIMARY: '#474646',
SECONDARY: '#232323',
},
SPACING: {
marginXs: '5px',
marginSm: '10px',
marginMd: '15px',
marginLg: '20px',
},
};

View file

@ -0,0 +1,139 @@
import React from 'react';
// tslint:disable-next-line: no-import-side-effect no-submodule-imports
// import 'reset-css/reset.css';
import { DefaultTheme, ThemeProvider } from 'styled-components';
const white = '#ffffff';
const black = '#000000';
const destructive = '#ff453a';
const accentLightTheme = '#00e97b';
const accentDarkTheme = '#00f782';
const borderLightTheme = '#f1f1f1';
const borderDarkTheme = '#ffffff0F';
const borderAvatarColor = '#00000059';
const commonThemes = {
fonts: {
sessionFontDefault: 'Public Sans',
sessionFontAccent: 'Loor',
sessionFontMono: 'SpaceMono',
},
};
export const lightTheme: DefaultTheme = {
commonThemes,
colors: {
accent: accentLightTheme,
accentButton: black,
destructive: destructive,
cellBackground: '#fcfcfc',
modalBackground: '#fcfcfc',
fakeChatBubbleBackground: '#f5f5f5',
// input
inputBackground: '#8E8E93331F',
// text
textColor: black,
textColorSubtle: '#a0a0a0',
textColorOpposite: white,
textHighlight: `${black}33`,
// inbox
inboxBackground: white,
// buttons
backgroundPrimary: '#272726',
foregroundPrimary: white,
buttonGreen: '#272726',
// conversation view
composeViewBackground: '#fcfcfc',
composeViewTextFieldBackground: '#ededed',
receivedMessageBackground: '#f5f5f5',
sentMessageBackground: accentLightTheme,
receivedMessageText: black,
sentMessageText: black,
sessionShadow: `0 0 4px 0 ${black}5E`,
sessionShadowColor: `${black}5E`,
// left pane
conversationList: white,
conversationItemHasUnread: '#fcfcfc',
conversationItemSelected: '#f0f0f0',
clickableHovered: '#dfdfdf',
sessionBorder: `1px solid ${borderLightTheme}`,
sessionUnreadBorder: `4px solid ${accentLightTheme}`,
leftpaneOverlayBackground: white,
// scrollbars
scrollBarTrack: '#fcfcfc',
scrollBarThumb: '#474646',
// pill divider:
pillDividerColor: `${black}1A`,
pillDividerTextColor: '#555555',
// context menu
contextMenuBackground: '#f5f5f5',
filterSessionText: 'brightness(0) saturate(100%)',
lastSeenIndicatorColor: '#62656a',
lastSeenIndicatorTextColor: '#070c14',
quoteBottomBarBackground: '#f0f0f0',
},
};
export const darkTheme = {
commonThemes,
colors: {
accent: accentDarkTheme,
accentButton: accentDarkTheme,
destructive: destructive,
cellBackground: '#1b1b1b',
modalBackground: '#101011',
fakeChatBubbleBackground: '#212121',
// input
inputBackground: '#8e8e931F',
// text
textColor: white,
textColorSubtle: '#a0a0a0',
textColorOpposite: black,
textHighlight: `${accentDarkTheme}99`,
// inbox
inboxBackground: 'linear-gradient(180deg, #171717 0%, #121212 100%)',
// buttons
backgroundPrimary: '#474646',
foregroundPrimary: white,
buttonGreen: accentDarkTheme,
// conversation view
composeViewBackground: '#1b1b1b',
composeViewTextFieldBackground: '#141414',
receivedMessageBackground: '#222325',
sentMessageBackground: '#3f4146',
receivedMessageText: white,
sentMessageText: white,
sessionShadow: `0 0 4px 0 ${white}33`,
sessionShadowColor: `${white}33`,
// left pane
conversationList: '#1b1b1b',
conversationItemHasUnread: '#2c2c2c',
conversationItemSelected: '#404040',
clickableHovered: '#414347',
sessionBorder: `1px solid ${borderDarkTheme}`,
sessionUnreadBorder: `4px solid ${accentDarkTheme}`,
leftpaneOverlayBackground:
'linear-gradient(180deg, #171717 0%, #121212 100%)',
// scrollbars
scrollBarTrack: '#1b1b1b',
scrollBarThumb: '#474646',
// pill divider:
pillDividerColor: '#353535',
pillDividerTextColor: '#a0a0a0',
// context menu
contextMenuBackground: '#212121',
filterSessionText: 'none',
lastSeenIndicatorColor: '#353535',
lastSeenIndicatorTextColor: '#a8a9aa',
quoteBottomBarBackground: '#404040',
},
};
export const Theme = ({
children,
theme,
}: {
children: any;
theme: DefaultTheme;
}) => <ThemeProvider theme={theme}>{children}</ThemeProvider>;

31
ts/state/ducks/theme.tsx Normal file
View file

@ -0,0 +1,31 @@
export const APPLY_THEME = 'APPLY_THEME';
export const applyTheme = (theme: any) => {
return {
type: APPLY_THEME,
payload: theme,
};
};
import { lightTheme } from './SessionTheme';
export type ThemeStateType = typeof lightTheme;
const initialState = lightTheme;
export const reducer = (
state: any = initialState,
{
type,
payload,
}: {
type: string;
payload: ThemeStateType;
}
): ThemeStateType => {
switch (type) {
case APPLY_THEME:
return payload;
default:
return state;
}
};

View file

@ -6,6 +6,7 @@ import {
reducer as conversations,
} from './ducks/conversations';
import { reducer as user, UserStateType } from './ducks/user';
import { reducer as theme, ThemeStateType } from './ducks/theme';
// import { reducer as messages } from './ducks/messages';
export type StateType = {
@ -13,6 +14,7 @@ export type StateType = {
messages: any;
conversations: ConversationsStateType;
user: UserStateType;
theme: ThemeStateType;
};
export const reducers = {
@ -22,6 +24,7 @@ export const reducers = {
messages: search,
conversations,
user,
theme,
};
// Making this work would require that our reducer signature supported AnyAction, not

View file

@ -21,7 +21,6 @@ const mapStateToProps = (state: StateType) => {
const leftPaneList = getLeftPaneLists(state);
const lists = showSearch ? undefined : leftPaneList;
const searchResults = showSearch ? getSearchResults(state) : undefined;
return {
...lists,
searchTerm: getQuery(state),
@ -32,6 +31,7 @@ const mapStateToProps = (state: StateType) => {
showArchived: getShowArchived(state),
i18n: getIntl(state),
unreadMessageCount: leftPaneList.unreadCount,
theme: state.theme,
};
};

View file

@ -39,6 +39,7 @@ const mapStateToProps = (state: StateType) => {
return {
conversations: state.conversations,
theme: state.theme,
};
};

63
ts/styled.d.ts vendored Normal file
View file

@ -0,0 +1,63 @@
import 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme {
commonThemes: {
fonts: {
sessionFontDefault: string;
sessionFontAccent: string;
sessionFontMono: string;
};
};
colors: {
accent: string;
accentButton: string;
destructive: string;
cellBackground: string;
modalBackground: string;
fakeChatBubbleBackground: string;
// input
inputBackground: string;
// text
textColor: string;
textColorSubtle: string;
textColorOpposite: string;
textHighlight: string;
// inbox
inboxBackground: string;
// buttons
backgroundPrimary: string;
foregroundPrimary: string;
buttonGreen: string;
// conversation view
composeViewBackground: string;
composeViewTextFieldBackground: string;
receivedMessageBackground: string;
sentMessageBackground: string;
receivedMessageText: string;
sentMessageText: string;
sessionShadow: string;
sessionShadowColor: string;
// left pane
conversationList: string;
conversationItemHasUnread: string;
conversationItemSelected: string;
clickableHovered: string;
sessionBorder: string;
sessionUnreadBorder: string;
leftpaneOverlayBackground: string;
// scrollbars
scrollBarTrack: string;
scrollBarThumb: string;
// pill divider:
pillDividerColor: string;
pillDividerTextColor: string;
// context menu
contextMenuBackground: string;
filterSessionText: string;
lastSeenIndicatorColor: string;
lastSeenIndicatorTextColor: string;
quoteBottomBarBackground: string;
};
}
}

4
ts/window.d.ts vendored
View file

@ -12,7 +12,7 @@ import { LibTextsecure } from '../libtextsecure';
import { ConversationType } from '../js/modules/data';
import { RecoveryPhraseUtil } from '../libloki/modules/mnemonic';
import { ConfirmationDialogParams } from '../background';
import {} from 'styled-components/cssprop';
/*
We declare window stuff here instead of global.d.ts because we are importing other declarations.
If you import anything in global.d.ts, the type system won't work correctly.
@ -89,7 +89,7 @@ declare global {
toggleMediaPermissions: any;
toggleMenuBar: any;
toggleSpellCheck: any;
toggleTheme: any;
setTheme: (newTheme: string) => any;
tokenlessFileServerAdnAPI: LokiAppDotNetServerInterface;
userConfig: any;
versionInfo: any;