From 5d0b26df75a974708a3693be4d2688f8f14c6fdb Mon Sep 17 00:00:00 2001 From: Onokaev Date: Mon, 12 Jul 2021 11:17:49 +0300 Subject: [PATCH 1/8] feature automatic dark mode on app launch --- src/app/views/App.tsx | 228 +++++++++++++++++++++++++++--------------- 1 file changed, 147 insertions(+), 81 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index f59c770ec..649f93b72 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -1,10 +1,12 @@ import { Announced, - IStackTokens, ITheme, styled + IStackTokens, + ITheme, + styled, } from 'office-ui-fabric-react'; import React, { Component } from 'react'; import { InjectedIntl, injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; +import { connect, useDispatch } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { geLocale } from '../../appLocale'; @@ -13,7 +15,11 @@ import { componentNames, eventTypes, telemetry } from '../../telemetry'; import { loadGETheme } from '../../themes'; import { ThemeContext } from '../../themes/theme-context'; import { Mode } from '../../types/enums'; -import { IInitMessage, IQuery, IThemeChangedMessage } from '../../types/query-runner'; +import { + IInitMessage, + IQuery, + IThemeChangedMessage, +} from '../../types/query-runner'; import { IRootState } from '../../types/root'; import { ISharedQueryParams } from '../../types/share-query'; import { ISidebarProps } from '../../types/sidebar'; @@ -22,13 +28,19 @@ import { runQuery } from '../services/actions/query-action-creators'; import { setSampleQuery } from '../services/actions/query-input-action-creators'; import { clearQueryStatus } from '../services/actions/query-status-action-creator'; import { clearTermsOfUse } from '../services/actions/terms-of-use-action-creator'; -import { changeThemeSuccess } from '../services/actions/theme-action-creator'; +import { + changeTheme, + changeThemeSuccess, +} from '../services/actions/theme-action-creator'; import { toggleSidebar } from '../services/actions/toggle-sidebar-action-creator'; import { GRAPH_URL } from '../services/graph-constants'; import { parseSampleUrl } from '../utils/sample-url-generation'; import { substituteTokens } from '../utils/token-helpers'; import { translateMessage } from '../utils/translate-messages'; -import { appTitleDisplayOnFullScreen, appTitleDisplayOnMobileScreen } from './app-sections/AppTitle'; +import { + appTitleDisplayOnFullScreen, + appTitleDisplayOnMobileScreen, +} from './app-sections/AppTitle'; import { headerMessaging } from './app-sections/HeaderMessaging'; import { statusMessages } from './app-sections/StatusMessages'; import { termsOfUseMessage } from './app-sections/TermsOfUseMessage'; @@ -78,7 +90,7 @@ class App extends Component { this.state = { selectedVerb: 'GET', mobileScreen: false, - hideDialog: true + hideDialog: true, }; } @@ -89,6 +101,23 @@ class App extends Component { const urlParams = new URLSearchParams(window.location.search); const sessionId = urlParams.get('sid'); + const currentSystemTheme = window.matchMedia( + '(prefers-color-scheme: dark)' + ); + let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme + + if (currentSystemTheme.matches === true) { + // dark theme + currentTheme = 'dark'; + } else if (currentSystemTheme.matches === false) { + // light theme + } else { + //high contrast theme + currentTheme = 'high-contrast'; + } + + changeTheme(currentTheme); + if (sessionId) { const authResp = await authenticationWrapper.logIn(sessionId); if (authResp) { @@ -103,7 +132,7 @@ class App extends Component { 'https://docs.microsoft.com', 'https://review.docs.microsoft.com', 'https://ppe.docs.microsoft.com', - 'https://docs.azure.cn' + 'https://docs.azure.cn', ]; // Notify host document that GE is ready to receive messages @@ -147,7 +176,8 @@ class App extends Component { } private generateQueryObjectFrom(queryParams: any) { - const { request, method, version, graphUrl, requestBody, headers } = queryParams; + const { request, method, version, graphUrl, requestBody, headers } = + queryParams; if (!request) { return null; @@ -158,7 +188,7 @@ class App extends Component { selectedVerb: method, selectedVersion: version, sampleBody: requestBody ? this.hashDecode(requestBody) : null, - sampleHeaders: (headers) ? JSON.parse(this.hashDecode(headers)) : [], + sampleHeaders: headers ? JSON.parse(this.hashDecode(headers)) : [], }; } @@ -214,7 +244,7 @@ class App extends Component { if (actions) { actions.setSampleQuery({ sampleUrl: url, - selectedVerb: verb + selectedVerb: verb, }); } @@ -232,7 +262,7 @@ class App extends Component { const requestHeaders = headers.map((header: any) => { return { name: Object.keys(header)[0], - value: Object.values(header)[0] + value: Object.values(header)[0], }; }); @@ -241,7 +271,7 @@ class App extends Component { selectedVerb: verb, sampleBody: body, selectedVersion: queryVersion, - sampleHeaders: requestHeaders + sampleHeaders: requestHeaders, }; substituteTokens(query, profile); @@ -249,12 +279,11 @@ class App extends Component { actions.setSampleQuery(query); } }, 1000); - }; public handleSelectVerb = (verb: string) => { this.setState({ - selectedVerb: verb + selectedVerb: verb, }); }; @@ -263,12 +292,10 @@ class App extends Component { const properties = { ...sidebarProperties }; properties.showSidebar = !properties.showSidebar; this.props.actions!.toggleSidebar(properties); - telemetry.trackEvent( - eventTypes.BUTTON_CLICK_EVENT, - { - ComponentName: componentNames.SIDEBAR_HAMBURGER_BUTTON - }); - } + telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, { + ComponentName: componentNames.SIDEBAR_HAMBURGER_BUTTON, + }); + }; public displayToggleButton = (mediaQueryList: any) => { const mobileScreen = mediaQueryList.matches; @@ -279,32 +306,45 @@ class App extends Component { const properties = { mobileScreen, - showSidebar + showSidebar, }; this.props.actions!.toggleSidebar(properties); - } + }; public displayAuthenticationSection = (minimised: boolean) => { - return
-
- -
-
- + return ( +
+
+ +
+
+ +
-
; - } + ); + }; public render() { const classes = classNames(this.props); - const { authenticated, graphExplorerMode, queryState, minimised, termsOfUse, sampleQuery, - actions, sidebarProperties, intl: { messages } }: any = this.props; + const { + authenticated, + graphExplorerMode, + queryState, + minimised, + termsOfUse, + sampleQuery, + actions, + sidebarProperties, + intl: { messages }, + }: any = this.props; const query = createShareLink(sampleQuery, authenticated); const sampleHeaderText = messages['Sample Queries']; // tslint:disable-next-line:no-string-literal @@ -320,7 +360,7 @@ class App extends Component { const stackTokens: IStackTokens = { childrenGap: 10, - padding: 10 + padding: 10, }; let sidebarWidth = `col-sm-12 col-lg-3 col-md-4 ${classes.sidebar}`; @@ -343,46 +383,62 @@ class App extends Component { // @ts-ignore
- +
{graphExplorerMode === Mode.Complete && (
- {mobileScreen && appTitleDisplayOnMobileScreen( - stackTokens, - classes, - this.toggleSidebar)} - - {!mobileScreen && appTitleDisplayOnFullScreen( - classes, - minimised, - this.toggleSidebar - )} + {mobileScreen && + appTitleDisplayOnMobileScreen( + stackTokens, + classes, + this.toggleSidebar + )} + + {!mobileScreen && + appTitleDisplayOnFullScreen( + classes, + minimised, + this.toggleSidebar + )}
{this.displayAuthenticationSection(minimised)}
- {showSidebar && <> - - } + {showSidebar && ( + <> + + + )}
)}
- {graphExplorerMode === Mode.TryIt && headerMessaging(classes, query)} - - {displayContent && <> -
- -
- {statusMessages(queryState, sampleQuery, actions)} - {termsOfUseMessage(termsOfUse, actions, classes, geLocale)} - { - // @ts-ignore - - } - } + {graphExplorerMode === Mode.TryIt && + headerMessaging(classes, query)} + + {displayContent && ( + <> +
+ +
+ {statusMessages(queryState, sampleQuery, actions)} + {termsOfUseMessage(termsOfUse, actions, classes, geLocale)} + { + // @ts-ignore + + } + + )}
@@ -391,8 +447,15 @@ class App extends Component { } } -const mapStateToProps = ({ sidebarProperties, theme, - queryRunnerStatus, profile, sampleQuery, termsOfUse, authToken, graphExplorerMode +const mapStateToProps = ({ + sidebarProperties, + theme, + queryRunnerStatus, + profile, + sampleQuery, + termsOfUse, + authToken, + graphExplorerMode, }: IRootState) => { const mobileScreen = !!sidebarProperties.mobileScreen; const showSidebar = !!sidebarProperties.showSidebar; @@ -407,23 +470,26 @@ const mapStateToProps = ({ sidebarProperties, theme, termsOfUse, minimised: !mobileScreen && !showSidebar, sampleQuery, - authenticated: !!authToken.token + authenticated: !!authToken.token, }; }; const mapDispatchToProps = (dispatch: Dispatch) => { return { - actions: bindActionCreators({ - clearQueryStatus, - clearTermsOfUse, - runQuery, - setSampleQuery, - toggleSidebar, - ...authActionCreators, - changeTheme: (newTheme: string) => { - return (disp: Function) => disp(changeThemeSuccess(newTheme)); - } - }, dispatch) + actions: bindActionCreators( + { + clearQueryStatus, + clearTermsOfUse, + runQuery, + setSampleQuery, + toggleSidebar, + ...authActionCreators, + changeTheme: (newTheme: string) => { + return (disp: Function) => disp(changeThemeSuccess(newTheme)); + }, + }, + dispatch + ), }; }; @@ -431,4 +497,4 @@ const StyledApp = styled(App, appStyles as any); const IntlApp = injectIntl(StyledApp); //@ts-ignore -export default connect(mapStateToProps, mapDispatchToProps)(IntlApp); \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(IntlApp); From 5a002c38703c778c05408416f23b828451765b2b Mon Sep 17 00:00:00 2001 From: Onokaev Date: Mon, 12 Jul 2021 11:31:14 +0300 Subject: [PATCH 2/8] refactored previous commit's code --- src/app/views/App.tsx | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index 649f93b72..e02c06e90 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -101,23 +101,6 @@ class App extends Component { const urlParams = new URLSearchParams(window.location.search); const sessionId = urlParams.get('sid'); - const currentSystemTheme = window.matchMedia( - '(prefers-color-scheme: dark)' - ); - let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme - - if (currentSystemTheme.matches === true) { - // dark theme - currentTheme = 'dark'; - } else if (currentSystemTheme.matches === false) { - // light theme - } else { - //high contrast theme - currentTheme = 'high-contrast'; - } - - changeTheme(currentTheme); - if (sessionId) { const authResp = await authenticationWrapper.logIn(sessionId); if (authResp) { @@ -147,6 +130,23 @@ class App extends Component { // Listens for messages from host document window.addEventListener('message', this.receiveMessage, false); this.handleSharedQueries(); + + const currentSystemTheme = window.matchMedia( + '(prefers-color-scheme: dark)' + ); + let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme + + if (currentSystemTheme.matches === true) { + // dark theme + currentTheme = 'dark'; + } else if (currentSystemTheme.matches === false) { + // light theme + } else { + //high contrast theme + currentTheme = 'high-contrast'; + } + + changeTheme(currentTheme); }; public handleSharedQueries() { From b42f9651259c40ffc692d2afc097f44a14733351 Mon Sep 17 00:00:00 2001 From: Onokaev Date: Tue, 13 Jul 2021 09:39:59 +0300 Subject: [PATCH 3/8] refactored the code to a function that handles the application of the current-system-theme --- src/app/views/App.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index e02c06e90..d7a0bf0cb 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -6,7 +6,7 @@ import { } from 'office-ui-fabric-react'; import React, { Component } from 'react'; import { InjectedIntl, injectIntl } from 'react-intl'; -import { connect, useDispatch } from 'react-redux'; +import { connect } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { geLocale } from '../../appLocale'; @@ -131,6 +131,11 @@ class App extends Component { window.addEventListener('message', this.receiveMessage, false); this.handleSharedQueries(); + //Apply current system theme + this.applyCurrentSystemTheme(); + }; + + private applyCurrentSystemTheme(): void { const currentSystemTheme = window.matchMedia( '(prefers-color-scheme: dark)' ); @@ -147,7 +152,7 @@ class App extends Component { } changeTheme(currentTheme); - }; + } public handleSharedQueries() { const { actions } = this.props; From 9e4e83f7b4e50f876e81da4e7963da0a8b11dc3a Mon Sep 17 00:00:00 2001 From: Onokaev Date: Tue, 13 Jul 2021 12:21:27 +0300 Subject: [PATCH 4/8] added a listener to watch for theme changes and dynamically change the theme --- src/app/views/App.tsx | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index d7a0bf0cb..65519be04 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -132,26 +132,48 @@ class App extends Component { this.handleSharedQueries(); //Apply current system theme - this.applyCurrentSystemTheme(); + this.getCurrentSystemTheme(); }; - private applyCurrentSystemTheme(): void { + private getCurrentSystemTheme(): void { + let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme + const currentSystemTheme = window.matchMedia( '(prefers-color-scheme: dark)' ); - let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme if (currentSystemTheme.matches === true) { - // dark theme currentTheme = 'dark'; + this.applyCurrentSystemTheme(currentTheme); } else if (currentSystemTheme.matches === false) { - // light theme + currentTheme = 'light'; + this.applyCurrentSystemTheme(currentTheme); } else { - //high contrast theme currentTheme = 'high-contrast'; + this.applyCurrentSystemTheme(currentTheme); } + } + private applyCurrentSystemTheme( + currentTheme: IThemeChangedMessage['theme'] + ): void { changeTheme(currentTheme); + + //register an event listener to watch for system theme changes + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', function (newSystemTheme) { + if (newSystemTheme.matches === true) { + currentTheme = 'dark'; + changeTheme(currentTheme); + } else if (newSystemTheme.matches === false) { + currentTheme = 'light'; + changeTheme(currentTheme); + } else { + currentTheme = 'high-contrast'; + changeTheme(currentTheme); + } + }); } public handleSharedQueries() { From 11b721feea20a301d6669a769a926d1c916cf3bb Mon Sep 17 00:00:00 2001 From: Onokaev Date: Wed, 14 Jul 2021 17:20:43 +0300 Subject: [PATCH 5/8] fixed type errors in previous commit. Saved current theme is now persistent --- src/app/views/App.tsx | 67 +++++++++++++++++++-------------------- src/index.tsx | 55 ++++++++++++++++---------------- src/themes/theme-utils.ts | 9 ++---- 3 files changed, 63 insertions(+), 68 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index 65519be04..0b536907c 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -14,6 +14,7 @@ import { authenticationWrapper } from '../../modules/authentication'; import { componentNames, eventTypes, telemetry } from '../../telemetry'; import { loadGETheme } from '../../themes'; import { ThemeContext } from '../../themes/theme-context'; +import { readTheme, saveTheme } from '../../themes/theme-utils'; import { Mode } from '../../types/enums'; import { IInitMessage, @@ -28,10 +29,7 @@ import { runQuery } from '../services/actions/query-action-creators'; import { setSampleQuery } from '../services/actions/query-input-action-creators'; import { clearQueryStatus } from '../services/actions/query-status-action-creator'; import { clearTermsOfUse } from '../services/actions/terms-of-use-action-creator'; -import { - changeTheme, - changeThemeSuccess, -} from '../services/actions/theme-action-creator'; +import { changeThemeSuccess } from '../services/actions/theme-action-creator'; import { toggleSidebar } from '../services/actions/toggle-sidebar-action-creator'; import { GRAPH_URL } from '../services/graph-constants'; import { parseSampleUrl } from '../utils/sample-url-generation'; @@ -73,6 +71,7 @@ interface IAppProps { toggleSidebar: Function; signIn: Function; storeScopes: Function; + changeTheme: Function; }; } @@ -131,49 +130,49 @@ class App extends Component { window.addEventListener('message', this.receiveMessage, false); this.handleSharedQueries(); - //Apply current system theme - this.getCurrentSystemTheme(); + this.setCurrentSystemTheme(); }; - private getCurrentSystemTheme(): void { - let currentTheme: IThemeChangedMessage['theme'] = 'light'; //default theme + private setCurrentSystemTheme(): void { + const themeFromLocalStorage = readTheme(); + let currentTheme: string = 'light'; + + if (themeFromLocalStorage) { + currentTheme = themeFromLocalStorage; + } else { + currentTheme = this.checkTheme(); + } + + this.applyCurrentSystemTheme(currentTheme); + } - const currentSystemTheme = window.matchMedia( + private checkTheme(): string { + let currentTheme: string = 'light'; + const currentSystemThemeDark = window.matchMedia( '(prefers-color-scheme: dark)' ); - if (currentSystemTheme.matches === true) { + const currentSystemThemeLight = window.matchMedia( + '(prefers-color-scheme: light)' + ); + + if (currentSystemThemeDark.matches === true) { currentTheme = 'dark'; - this.applyCurrentSystemTheme(currentTheme); - } else if (currentSystemTheme.matches === false) { + } else if (currentSystemThemeLight.matches === true) { currentTheme = 'light'; - this.applyCurrentSystemTheme(currentTheme); } else { currentTheme = 'high-contrast'; - this.applyCurrentSystemTheme(currentTheme); } + + return currentTheme; } - private applyCurrentSystemTheme( - currentTheme: IThemeChangedMessage['theme'] - ): void { - changeTheme(currentTheme); - - //register an event listener to watch for system theme changes - window - .matchMedia('(prefers-color-scheme: dark)') - .addEventListener('change', function (newSystemTheme) { - if (newSystemTheme.matches === true) { - currentTheme = 'dark'; - changeTheme(currentTheme); - } else if (newSystemTheme.matches === false) { - currentTheme = 'light'; - changeTheme(currentTheme); - } else { - currentTheme = 'high-contrast'; - changeTheme(currentTheme); - } - }); + private applyCurrentSystemTheme(currentTheme: string): void { + loadGETheme(currentTheme); + + // @ts-ignore + this.props.actions!.changeTheme(currentTheme); + saveTheme(currentTheme); } public handleSharedQueries() { diff --git a/src/index.tsx b/src/index.tsx index 5daf9cd2d..5b106b7bb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,7 +14,10 @@ import pt from 'react-intl/locale-data/pt'; import ru from 'react-intl/locale-data/ru'; import zh from 'react-intl/locale-data/zh'; import { Provider } from 'react-redux'; -import { getAuthTokenSuccess, getConsentedScopesSuccess } from './app/services/actions/auth-action-creators'; +import { + getAuthTokenSuccess, + getConsentedScopesSuccess, +} from './app/services/actions/auth-action-creators'; import { setDevxApiUrl } from './app/services/actions/devxApi-action-creators'; import { setGraphExplorerMode } from './app/services/actions/explorer-mode-action-creator'; import { addHistoryItem } from './app/services/actions/request-history-action-creators'; @@ -49,7 +52,7 @@ if (apiExplorer) { initializeIcons(); -const currentTheme = readTheme(); +const currentTheme = readTheme() || 'light'; loadGETheme(currentTheme); const appState: any = store({ @@ -67,33 +70,26 @@ const appState: any = store({ }, termsOfUse: true, theme: currentTheme, - }); function refreshAccessToken() { - authenticationWrapper.getToken().then((authResponse: AuthenticationResult) => { - if (authResponse && authResponse.accessToken) { - appState.dispatch(getAuthTokenSuccess(true)); - appState.dispatch(getConsentedScopesSuccess(authResponse.scopes)); - } - }).catch(() => { - // ignore the error as it means that a User login is required - }); + authenticationWrapper + .getToken() + .then((authResponse: AuthenticationResult) => { + if (authResponse && authResponse.accessToken) { + appState.dispatch(getAuthTokenSuccess(true)); + appState.dispatch(getConsentedScopesSuccess(authResponse.scopes)); + } + }) + .catch(() => { + // ignore the error as it means that a User login is required + }); } refreshAccessToken(); setInterval(refreshAccessToken, 1000 * 60 * 10); // refresh access token every 10 minutes -addLocaleData([ - ...pt, - ...de, - ...en, - ...fr, - ...jp, - ...ru, - ...zh, - ...es, -]); +addLocaleData([...pt, ...de, ...en, ...fr, ...jp, ...ru, ...zh, ...es]); const theme = new URLSearchParams(location.search).get('theme'); @@ -114,7 +110,7 @@ if (devxApiUrl && isValidHttpsUrl(devxApiUrl)) { const devxApi: IDevxAPI = { baseUrl: devxApiUrl, - parameters: '' + parameters: '', }; if (org && branchName) { @@ -136,7 +132,7 @@ readHistoryData().then((data: any) => { */ enum Workers { Json = 'json', - Editor = 'editor' + Editor = 'editor', } (window as any).MonacoEnvironment = { @@ -145,16 +141,16 @@ enum Workers { return getWorkerFor(Workers.Json); } return getWorkerFor(Workers.Editor); - } + }, }; function getWorkerFor(worker: string): string { // tslint:disable-next-line:max-line-length - const WORKER_PATH = 'https://graphstagingblobstorage.blob.core.windows.net/staging/vendor/bower_components/explorer-v2/build'; + const WORKER_PATH = + 'https://graphstagingblobstorage.blob.core.windows.net/staging/vendor/bower_components/explorer-v2/build'; return `data:text/javascript;charset=utf-8,${encodeURIComponent(` - importScripts('${WORKER_PATH}/${worker}.worker.js');` - )}`; + importScripts('${WORKER_PATH}/${worker}.worker.js');`)}`; } const telemetryProvider: ITelemetry = telemetry; @@ -163,7 +159,10 @@ telemetryProvider.initialize(); const Root = () => { return ( - + diff --git a/src/themes/theme-utils.ts b/src/themes/theme-utils.ts index b7a51b211..a66a397c3 100644 --- a/src/themes/theme-utils.ts +++ b/src/themes/theme-utils.ts @@ -1,13 +1,10 @@ -import { IThemeChangedMessage } from '../types/query-runner'; - const key = 'CURRENT_THEME'; -const defaultTheme = 'light'; -export function saveTheme(theme: IThemeChangedMessage['theme']) { +export function saveTheme(theme: string) { localStorage.setItem(key, theme); } export function readTheme() { - const theme = localStorage.getItem(key) || defaultTheme; + const theme = localStorage.getItem(key); return theme; -} \ No newline at end of file +} From 56ca1450c0464f2ac7ab9453d6cb43ab09f4f361 Mon Sep 17 00:00:00 2001 From: Onokaev Date: Wed, 14 Jul 2021 20:23:35 +0300 Subject: [PATCH 6/8] refactored changeTheme action creator --- src/app/views/App.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index 0b536907c..cc890b564 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -14,7 +14,7 @@ import { authenticationWrapper } from '../../modules/authentication'; import { componentNames, eventTypes, telemetry } from '../../telemetry'; import { loadGETheme } from '../../themes'; import { ThemeContext } from '../../themes/theme-context'; -import { readTheme, saveTheme } from '../../themes/theme-utils'; +import { readTheme } from '../../themes/theme-utils'; import { Mode } from '../../types/enums'; import { IInitMessage, @@ -29,7 +29,7 @@ import { runQuery } from '../services/actions/query-action-creators'; import { setSampleQuery } from '../services/actions/query-input-action-creators'; import { clearQueryStatus } from '../services/actions/query-status-action-creator'; import { clearTermsOfUse } from '../services/actions/terms-of-use-action-creator'; -import { changeThemeSuccess } from '../services/actions/theme-action-creator'; +import { changeTheme } from '../services/actions/theme-action-creator'; import { toggleSidebar } from '../services/actions/toggle-sidebar-action-creator'; import { GRAPH_URL } from '../services/graph-constants'; import { parseSampleUrl } from '../utils/sample-url-generation'; @@ -172,7 +172,6 @@ class App extends Component { // @ts-ignore this.props.actions!.changeTheme(currentTheme); - saveTheme(currentTheme); } public handleSharedQueries() { @@ -510,9 +509,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => { setSampleQuery, toggleSidebar, ...authActionCreators, - changeTheme: (newTheme: string) => { - return (disp: Function) => disp(changeThemeSuccess(newTheme)); - }, + changeTheme, }, dispatch ), From 56197462a4edbf3365771256d4376de5a44c46c1 Mon Sep 17 00:00:00 2001 From: Onokaev Date: Fri, 16 Jul 2021 13:01:57 +0300 Subject: [PATCH 7/8] refactored automatic theme mode to index.tsx --- .../services/actions/theme-action-creator.ts | 4 +- src/app/views/App.tsx | 44 --------- src/app/views/settings/Settings.tsx | 92 +++++++++---------- src/index.tsx | 44 ++++++++- 4 files changed, 85 insertions(+), 99 deletions(-) diff --git a/src/app/services/actions/theme-action-creator.ts b/src/app/services/actions/theme-action-creator.ts index 3193f264a..b8b476ec2 100644 --- a/src/app/services/actions/theme-action-creator.ts +++ b/src/app/services/actions/theme-action-creator.ts @@ -2,7 +2,6 @@ import { Dispatch } from 'redux'; import { saveTheme } from '../../../themes/theme-utils'; import { IAction } from '../../../types/action'; -import { IThemeChangedMessage } from '../../../types/query-runner'; import { CHANGE_THEME_SUCCESS } from '../redux-constants'; export function changeThemeSuccess(response: string): IAction { @@ -12,8 +11,7 @@ export function changeThemeSuccess(response: string): IAction { }; } -export function changeTheme(theme: IThemeChangedMessage['theme']): Function { - +export function changeTheme(theme: string): Function { saveTheme(theme); return (dispatch: Dispatch) => { diff --git a/src/app/views/App.tsx b/src/app/views/App.tsx index cc890b564..86669cc0d 100644 --- a/src/app/views/App.tsx +++ b/src/app/views/App.tsx @@ -14,7 +14,6 @@ import { authenticationWrapper } from '../../modules/authentication'; import { componentNames, eventTypes, telemetry } from '../../telemetry'; import { loadGETheme } from '../../themes'; import { ThemeContext } from '../../themes/theme-context'; -import { readTheme } from '../../themes/theme-utils'; import { Mode } from '../../types/enums'; import { IInitMessage, @@ -129,51 +128,8 @@ class App extends Component { // Listens for messages from host document window.addEventListener('message', this.receiveMessage, false); this.handleSharedQueries(); - - this.setCurrentSystemTheme(); }; - private setCurrentSystemTheme(): void { - const themeFromLocalStorage = readTheme(); - let currentTheme: string = 'light'; - - if (themeFromLocalStorage) { - currentTheme = themeFromLocalStorage; - } else { - currentTheme = this.checkTheme(); - } - - this.applyCurrentSystemTheme(currentTheme); - } - - private checkTheme(): string { - let currentTheme: string = 'light'; - const currentSystemThemeDark = window.matchMedia( - '(prefers-color-scheme: dark)' - ); - - const currentSystemThemeLight = window.matchMedia( - '(prefers-color-scheme: light)' - ); - - if (currentSystemThemeDark.matches === true) { - currentTheme = 'dark'; - } else if (currentSystemThemeLight.matches === true) { - currentTheme = 'light'; - } else { - currentTheme = 'high-contrast'; - } - - return currentTheme; - } - - private applyCurrentSystemTheme(currentTheme: string): void { - loadGETheme(currentTheme); - - // @ts-ignore - this.props.actions!.changeTheme(currentTheme); - } - public handleSharedQueries() { const { actions } = this.props; const queryStringParams = this.getQueryStringParams(); diff --git a/src/app/views/settings/Settings.tsx b/src/app/views/settings/Settings.tsx index fa6a5a203..622445cf0 100644 --- a/src/app/views/settings/Settings.tsx +++ b/src/app/views/settings/Settings.tsx @@ -11,7 +11,7 @@ import { Panel, PanelType, PrimaryButton, - TooltipHost + TooltipHost, } from 'office-ui-fabric-react'; import React, { useEffect, useState } from 'react'; import { FormattedMessage, injectIntl } from 'react-intl'; @@ -30,17 +30,20 @@ import { togglePermissionsPanel } from '../../services/actions/permissions-panel import { changeTheme } from '../../services/actions/theme-action-creator'; import { Permission } from '../query-runner/request/permissions'; - function Settings(props: ISettingsProps) { const dispatch = useDispatch(); - const { permissionsPanelOpen, authToken, theme: appTheme } = useSelector((state: IRootState) => state); + const { + permissionsPanelOpen, + authToken, + theme: appTheme, + } = useSelector((state: IRootState) => state); const authenticated = authToken.token; const [themeChooserDialogHidden, hideThemeChooserDialog] = useState(true); const [items, setItems] = useState([]); const [selectedPermissions, setSelectedPermissions] = useState([]); const { - intl: { messages } + intl: { messages }, }: any = props; useEffect(() => { @@ -68,7 +71,7 @@ function Settings(props: ISettingsProps) { { key: 'divider', text: '-', - itemType: DropdownMenuItemType.Divider + itemType: DropdownMenuItemType.Divider, }, { key: 'change-theme', @@ -77,7 +80,7 @@ function Settings(props: ISettingsProps) { iconName: 'Color', }, onClick: () => toggleThemeChooserDialogState(), - } + }, ]; if (authenticated) { @@ -97,7 +100,7 @@ function Settings(props: ISettingsProps) { iconName: 'SignOut', }, onClick: () => handleSignOut(), - }, + } ); } setItems(menuItems); @@ -107,11 +110,9 @@ function Settings(props: ISettingsProps) { let hidden = themeChooserDialogHidden; hidden = !hidden; hideThemeChooserDialog(hidden); - telemetry.trackEvent( - eventTypes.BUTTON_CLICK_EVENT, - { - ComponentName: componentNames.THEME_CHANGE_BUTTON - }); + telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, { + ComponentName: componentNames.THEME_CHANGE_BUTTON, + }); }; const handleSignOut = () => { @@ -119,15 +120,13 @@ function Settings(props: ISettingsProps) { }; const handleChangeTheme = (selectedTheme: any) => { - const newTheme: AppTheme = selectedTheme.key; + const newTheme: string = selectedTheme.key; dispatch(changeTheme(newTheme)); loadGETheme(newTheme); - telemetry.trackEvent( - eventTypes.BUTTON_CLICK_EVENT, - { - ComponentName: componentNames.SELECT_THEME_BUTTON, - SelectedTheme: selectedTheme.key.replace('-', ' ').toSentenceCase() - }); + telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, { + ComponentName: componentNames.SELECT_THEME_BUTTON, + SelectedTheme: selectedTheme.key.replace('-', ' ').toSentenceCase(), + }); }; const changePanelState = () => { @@ -139,21 +138,16 @@ function Settings(props: ISettingsProps) { }; const trackSelectPermissionsButtonClickEvent = () => { - telemetry.trackEvent( - eventTypes.BUTTON_CLICK_EVENT, - { - ComponentName: componentNames.VIEW_ALL_PERMISSIONS_BUTTON - }); - } + telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, { + ComponentName: componentNames.VIEW_ALL_PERMISSIONS_BUTTON, + }); + }; const trackReportAnIssueLinkClickEvent = () => { - telemetry.trackEvent( - eventTypes.LINK_CLICK_EVENT, - { - ComponentName: componentNames.REPORT_AN_ISSUE_LINK - }); - } - + telemetry.trackEvent(eventTypes.LINK_CLICK_EVENT, { + ComponentName: componentNames.REPORT_AN_ISSUE_LINK, + }); + }; const setPermissions = (permissions: []) => { setSelectedPermissions(permissions); @@ -165,11 +159,9 @@ function Settings(props: ISettingsProps) { }; const trackOfficeDevProgramLinkClickEvent = () => { - telemetry.trackEvent( - eventTypes.LINK_CLICK_EVENT, - { - ComponentName: componentNames.OFFICE_DEV_PROGRAM_LINK - }); + telemetry.trackEvent(eventTypes.LINK_CLICK_EVENT, { + ComponentName: componentNames.OFFICE_DEV_PROGRAM_LINK, + }); }; const getSelectionDetails = () => { @@ -206,7 +198,7 @@ function Settings(props: ISettingsProps) { const menuProperties = { shouldFocusOnMount: true, alignTargetEdge: true, - items + items, }; return ( @@ -214,17 +206,18 @@ function Settings(props: ISettingsProps) { + calloutProps={{ gapSpace: 0 }} + > - + menuProps={menuProperties} + />
- handleChangeTheme(selectedTheme)} + onChange={(event, selectedTheme) => + handleChangeTheme(selectedTheme) + } /> toggleThemeChooserDialogState()} /> + onClick={() => toggleThemeChooserDialogState()} + /> @@ -284,4 +279,3 @@ function Settings(props: ISettingsProps) { } export default injectIntl(Settings); - diff --git a/src/index.tsx b/src/index.tsx index ee59759d7..22e679d1b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -23,7 +23,6 @@ import { setGraphExplorerMode } from './app/services/actions/explorer-mode-actio import { getGraphProxyUrl } from './app/services/actions/proxy-action-creator'; import { addHistoryItem } from './app/services/actions/request-history-action-creators'; import { changeThemeSuccess } from './app/services/actions/theme-action-creator'; -import { GRAPH_API_SANDBOX_URL } from './app/services/graph-constants'; import { isValidHttpsUrl } from './app/utils/external-link-validation'; import App from './app/views/App'; import { readHistoryData } from './app/views/sidebar/history/history-utils'; @@ -39,6 +38,7 @@ import { readTheme } from './themes/theme-utils'; import { IDevxAPI } from './types/devx-api'; import { Mode } from './types/enums'; import { IHistoryItem } from './types/history'; +import { changeTheme } from './app/services/actions/theme-action-creator'; // removes the loading spinner from GE html after the app is loaded const spinner = document.getElementById('spinner'); @@ -54,8 +54,45 @@ if (apiExplorer) { initializeIcons(); -const currentTheme = readTheme() || 'light'; -loadGETheme(currentTheme); +let currentTheme = readTheme() || 'light'; +function setCurrentSystemTheme(): void { + const themeFromLocalStorage = readTheme(); + + if (themeFromLocalStorage) { + currentTheme = themeFromLocalStorage; + } else { + currentTheme = checkTheme(); + } + + applyCurrentSystemTheme(currentTheme); +} +function checkTheme(): string { + let currentSystemTheme: string = 'light'; + const currentSystemThemeDark = window.matchMedia( + '(prefers-color-scheme: dark)' + ); + + const currentSystemThemeLight = window.matchMedia( + '(prefers-color-scheme: light)' + ); + + if (currentSystemThemeDark.matches === true) { + currentSystemTheme = 'dark'; + } else if (currentSystemThemeLight.matches === true) { + currentSystemTheme = 'light'; + } else { + currentSystemTheme = 'high-contrast'; + } + + return currentSystemTheme; +} + +function applyCurrentSystemTheme(themeToApply: string): void { + loadGETheme(themeToApply); + + // @ts-ignore + appState.dispatch(changeTheme(themeToApply)); +} const appState: any = store({ authToken: { token: false, pending: false }, @@ -74,6 +111,7 @@ const appState: any = store({ theme: currentTheme, }); +setCurrentSystemTheme(); appState.dispatch(getGraphProxyUrl()); function refreshAccessToken() { From bde1ab323ceaf8231fc2757cad042e63a463fef5 Mon Sep 17 00:00:00 2001 From: Onokaev Date: Mon, 26 Jul 2021 22:47:35 +0300 Subject: [PATCH 8/8] renamed checkTheme fxn to getOSTheme --- src/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 22e679d1b..e60fe510d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -61,12 +61,12 @@ function setCurrentSystemTheme(): void { if (themeFromLocalStorage) { currentTheme = themeFromLocalStorage; } else { - currentTheme = checkTheme(); + currentTheme = getOSTheme(); } applyCurrentSystemTheme(currentTheme); } -function checkTheme(): string { +function getOSTheme(): string { let currentSystemTheme: string = 'light'; const currentSystemThemeDark = window.matchMedia( '(prefers-color-scheme: dark)'