From f57980f052f48c3935a35a5c85a896bf10268d14 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Thu, 26 Sep 2024 12:07:56 -0400 Subject: [PATCH 1/5] Enable dark mode for everyone and re-enable the system mode --- ui/frontend/ConfigMenu.tsx | 16 ++++++---------- ui/frontend/configureStore.ts | 2 +- ui/frontend/index.module.css | 2 -- ui/frontend/reducers/featureFlags.ts | 11 +---------- ui/frontend/selectors/index.ts | 2 -- 5 files changed, 8 insertions(+), 25 deletions(-) diff --git a/ui/frontend/ConfigMenu.tsx b/ui/frontend/ConfigMenu.tsx index beca9a779..8f9727ce2 100644 --- a/ui/frontend/ConfigMenu.tsx +++ b/ui/frontend/ConfigMenu.tsx @@ -16,7 +16,6 @@ import { ProcessAssembly, Theme, } from './types'; -import { showThemeSelector } from './selectors'; const MONACO_THEMES = [ 'vs', 'vs-dark', 'vscode-dark-plus', @@ -34,8 +33,6 @@ const ConfigMenu: React.FC = () => { const demangleAssembly = useAppSelector((state) => state.configuration.demangleAssembly); const processAssembly = useAppSelector((state) => state.configuration.processAssembly); - const showTheme = useAppSelector(showThemeSelector); - const dispatch = useAppDispatch(); const changeAceTheme = useCallback((t: string) => dispatch(config.changeAceTheme(t)), [dispatch]); const changeMonacoTheme = useCallback((t: string) => dispatch(config.changeMonacoTheme(t)), [dispatch]); @@ -104,13 +101,12 @@ const ConfigMenu: React.FC = () => { - {showTheme && ( - - { /* */ } - - - - )} + + + + + + { state.forced = true; state.showGemThreshold = ENABLED; - state.showThemeThreshold = ENABLED; }, forceDisableAll: (state) => { state.forced = true; state.showGemThreshold = DISABLED; - state.showThemeThreshold = DISABLED; }, }, extraReducers: (builder) => { @@ -47,15 +42,11 @@ const slice = createSlice({ return; } - const { showGemThreshold, showThemeThreshold } = action.payload; + const { showGemThreshold } = action.payload; if (showGemThreshold) { state.showGemThreshold = showGemThreshold; } - - if (showThemeThreshold) { - state.showThemeThreshold = showThemeThreshold; - } }); }, }); diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index ac9ae8a01..f146c989c 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -444,13 +444,11 @@ const websocket = (state: State) => state.websocket; const clientFeatureFlagThreshold = createSelector(client, (c) => c.featureFlagThreshold); const showGemThreshold = createSelector(featureFlags, ff => ff.showGemThreshold); -const showThemeThreshold = createSelector(featureFlags, ff => ff.showThemeThreshold); const createFeatureFlagSelector = (ff: (state: State) => number) => createSelector(clientFeatureFlagThreshold, ff, (c, ff) => c <= ff); export const showGemSelector = createFeatureFlagSelector(showGemThreshold); -export const showThemeSelector = createFeatureFlagSelector(showThemeThreshold); export const executeViaWebsocketSelector = createSelector(websocket, (ws) => ws.connected); From 59f5e7f3908c610b7525eb19bf92ffc6e6c36e66 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Thu, 26 Sep 2024 12:16:39 -0400 Subject: [PATCH 2/5] Short notifications should take the whole width --- ui/frontend/Notifications.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/frontend/Notifications.module.css b/ui/frontend/Notifications.module.css index 2b96f16ac..1c969ea38 100644 --- a/ui/frontend/Notifications.module.css +++ b/ui/frontend/Notifications.module.css @@ -20,6 +20,7 @@ $space: 0.25em; .notificationContent { padding: $space 0 $space $space; + width: 100%; } .close { From 19c06eaf1410f4411af817b0c5dae13fe105b7bc Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Thu, 26 Sep 2024 12:03:05 -0400 Subject: [PATCH 3/5] Add a notification for dark mode --- ui/frontend/Notifications.module.css | 4 ++ ui/frontend/Notifications.tsx | 54 ++++++++++++++++++++++++++- ui/frontend/reducers/configuration.ts | 17 +++++++++ ui/frontend/reducers/notifications.ts | 6 +++ ui/frontend/selectors/index.ts | 8 ++++ ui/frontend/types.ts | 1 + 6 files changed, 89 insertions(+), 1 deletion(-) diff --git a/ui/frontend/Notifications.module.css b/ui/frontend/Notifications.module.css index 1c969ea38..1023219a5 100644 --- a/ui/frontend/Notifications.module.css +++ b/ui/frontend/Notifications.module.css @@ -35,3 +35,7 @@ $space: 0.25em; padding-top: 0.5em; gap: 0.5em; } + +.swapTheme { + width: 100%; +} diff --git a/ui/frontend/Notifications.tsx b/ui/frontend/Notifications.tsx index 954833caf..b59db4115 100644 --- a/ui/frontend/Notifications.tsx +++ b/ui/frontend/Notifications.tsx @@ -3,9 +3,11 @@ import { Portal } from 'react-portal'; import { Close } from './Icon'; import { useAppDispatch, useAppSelector } from './hooks'; -import { seenRustSurvey2023 } from './reducers/notifications'; +import { swapTheme } from './reducers/configuration'; +import { seenDarkMode, seenRustSurvey2023 } from './reducers/notifications'; import { allowLongRun, wsExecuteKillCurrent } from './reducers/output/execute'; import * as selectors from './selectors'; +import { Theme } from './types'; import * as styles from './Notifications.module.css'; @@ -15,6 +17,7 @@ const Notifications: React.FC = () => { return (
+
@@ -22,6 +25,55 @@ const Notifications: React.FC = () => { ); }; +const DarkModeNotification: React.FC = () => { + const showIt = useAppSelector(selectors.showDarkModeSelector); + + const dispatch = useAppDispatch(); + const seenIt = useCallback(() => dispatch(seenDarkMode()), [dispatch]); + const swapToLight = useCallback(() => dispatch(swapTheme(Theme.Light)), [dispatch]); + const swapToDark = useCallback(() => dispatch(swapTheme(Theme.Dark)), [dispatch]); + const swapToSystem = useCallback(() => dispatch(swapTheme(Theme.System)), [dispatch]); + + return showIt ? ( + +

The playground now has a dark mode! Sample the themes here:

+ + + + + + + + + + + + + + + + +
+ + Use your system's preference
+ + The classic playground style
+ + Reduce the number of photons hitting your eyeballs
+ +

+ You can change the current UI theme (and the editor's theme) in the configuration menu. +

+
+ ) : null; +}; + const RustSurvey2023Notification: React.FC = () => { const showIt = useAppSelector(selectors.showRustSurvey2023Selector); diff --git a/ui/frontend/reducers/configuration.ts b/ui/frontend/reducers/configuration.ts index 5395602c7..a0524bd82 100644 --- a/ui/frontend/reducers/configuration.ts +++ b/ui/frontend/reducers/configuration.ts @@ -124,6 +124,22 @@ const slice = createSlice({ changeProcessAssembly: (state, action: PayloadAction) => { state.processAssembly = action.payload; }, + + swapTheme: (state, action: PayloadAction) => { + state.theme = action.payload; + switch (action.payload) { + case Theme.Light: { + state.ace.theme = 'github'; + state.monaco.theme = 'vs'; + break; + } + case Theme.Dark: { + state.ace.theme = 'github_dark'; + state.monaco.theme = 'vscode-dark-plus'; + break; + } + } + }, }, }); @@ -143,6 +159,7 @@ export const { changePairCharacters, changePrimaryAction, changeProcessAssembly, + swapTheme, } = slice.actions; export const changeEdition = diff --git a/ui/frontend/reducers/notifications.ts b/ui/frontend/reducers/notifications.ts index 1116e7b4d..f56135307 100644 --- a/ui/frontend/reducers/notifications.ts +++ b/ui/frontend/reducers/notifications.ts @@ -11,6 +11,7 @@ interface State { seenMonacoEditorAvailable: boolean; // expired seenRustSurvey2022: boolean; // expired seenRustSurvey2023: boolean; + seenDarkMode: boolean; } const initialState: State = { @@ -22,6 +23,7 @@ const initialState: State = { seenMonacoEditorAvailable: true, seenRustSurvey2022: true, seenRustSurvey2023: false, + seenDarkMode: false, }; const slice = createSlice({ @@ -30,6 +32,9 @@ const slice = createSlice({ reducers: { notificationSeen: (state, action: PayloadAction) => { switch (action.payload) { + case Notification.DarkMode: { + state.seenDarkMode = true; + } case Notification.RustSurvey2023: { state.seenRustSurvey2023 = true; } @@ -40,6 +45,7 @@ const slice = createSlice({ const { notificationSeen } = slice.actions; +export const seenDarkMode = () => notificationSeen(Notification.DarkMode); export const seenRustSurvey2023 = () => notificationSeen(Notification.RustSurvey2023); export default slice.reducer; diff --git a/ui/frontend/selectors/index.ts b/ui/frontend/selectors/index.ts index f146c989c..e3afbca9c 100644 --- a/ui/frontend/selectors/index.ts +++ b/ui/frontend/selectors/index.ts @@ -357,6 +357,13 @@ const notificationsSelector = (state: State) => state.notifications; const NOW = new Date(); +const DARK_MODE_END = new Date('2024-10-15T00:00:00Z'); +const DARK_MODE_OPEN = NOW <= DARK_MODE_END; +export const showDarkModeSelector = createSelector( + notificationsSelector, + notifications => DARK_MODE_OPEN && !notifications.seenDarkMode, +); + const RUST_SURVEY_2023_END = new Date('2024-01-15T00:00:00Z'); const RUST_SURVEY_2023_OPEN = NOW <= RUST_SURVEY_2023_END; export const showRustSurvey2023Selector = createSelector( @@ -365,6 +372,7 @@ export const showRustSurvey2023Selector = createSelector( ); export const anyNotificationsToShowSelector = createSelector( + showDarkModeSelector, showRustSurvey2023Selector, excessiveExecutionSelector, (...allNotifications) => allNotifications.some(n => n), diff --git a/ui/frontend/types.ts b/ui/frontend/types.ts index a028ab920..11d5fa7c2 100644 --- a/ui/frontend/types.ts +++ b/ui/frontend/types.ts @@ -165,5 +165,6 @@ export enum Focus { } export enum Notification { + DarkMode = 'dark-mode', RustSurvey2023 = 'rust-survey-2023', } From 31ec9177637d31f51462269c9724470678a36847 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Thu, 26 Sep 2024 13:58:08 -0400 Subject: [PATCH 4/5] Add a title to notification close button --- ui/frontend/Notifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/frontend/Notifications.tsx b/ui/frontend/Notifications.tsx index b59db4115..29902d3a8 100644 --- a/ui/frontend/Notifications.tsx +++ b/ui/frontend/Notifications.tsx @@ -121,7 +121,7 @@ interface NotificationProps { const Notification: React.FC = ({ onClose, children }) => (
{children}
-
From ed157312e90c6e0fd5dd23f34931b4c318f7c714 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Thu, 26 Sep 2024 13:59:01 -0400 Subject: [PATCH 5/5] Close notifications in unrelated tests --- tests/spec/features/assistance_spec.rb | 6 +++++- tests/spec/support/notifications.rb | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/spec/support/notifications.rb diff --git a/tests/spec/features/assistance_spec.rb b/tests/spec/features/assistance_spec.rb index b224136df..fe8a051cb 100644 --- a/tests/spec/features/assistance_spec.rb +++ b/tests/spec/features/assistance_spec.rb @@ -1,11 +1,15 @@ require 'spec_helper' require 'support/editor' +require 'support/notifications' require 'support/playground_actions' RSpec.feature "Editor assistance for common code modifications", type: :feature, js: true do include PlaygroundActions - before { visit '/' } + before do + visit '/' + Notifications.new(page).close_all + end scenario "building code without a main method offers adding one" do editor.set <<~EOF diff --git a/tests/spec/support/notifications.rb b/tests/spec/support/notifications.rb new file mode 100644 index 000000000..6b2ce183f --- /dev/null +++ b/tests/spec/support/notifications.rb @@ -0,0 +1,14 @@ +class Notifications + attr_reader :page + def initialize(page) + @page = page + end + + def close_all + page.all(:notification).each do |notification| + page.within(notification) do + page.click_on('dismiss notification') + end + end + end +end