mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
add theme logic to switch between dark/light theme based on redux store
This commit is contained in:
parent
6c7f1598b7
commit
1a379d2466
13 changed files with 284 additions and 40 deletions
|
@ -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 = () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
};
|
||||
|
|
139
ts/state/ducks/SessionTheme.tsx
Normal file
139
ts/state/ducks/SessionTheme.tsx
Normal 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
31
ts/state/ducks/theme.tsx
Normal 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;
|
||||
}
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ const mapStateToProps = (state: StateType) => {
|
|||
|
||||
return {
|
||||
conversations: state.conversations,
|
||||
theme: state.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
63
ts/styled.d.ts
vendored
Normal file
63
ts/styled.d.ts
vendored
Normal 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
4
ts/window.d.ts
vendored
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue