Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Remove ThemeProps and add new theme state logic #47864

Merged
merged 16 commits into from
Feb 22, 2023
Merged

Conversation

vovakulikov
Copy link
Contributor

@vovakulikov vovakulikov commented Feb 19, 2023

Before

Before this PR we had theme state on the top level in the Layout.tsx, we had a hook that provided an object with public API for theme (to read or to set a new value), there were at least two problems

  • Complex implementation of useThemeProps (rxjs to listen browser theme, mixed with logic around setting theme CSS classes)
  • Prop drilling, after we created these theme API on the top level, it was distributed via props

I think I don't need to dive deep in why prop drilling is a bad practice, but still, with prop drilling and nested components, we were updating the whole react tree when theme was changed, also passing these public Theme props required extending your component props with special type ThemeProps and this becomes a mess quickly if you extend something else (like one component props extended other component props)

Now

In this PR, we use more standard ways for global state distirubiotion - Context. In the new useTheme hook, we don't do any effects with CSS classes (consumer on the top level should handle it), so it's safe to call useTheme on any UI level. Privosly it wasn't safe, and because of this, we had this problem https://github.com/sourcegraph/sourcegraph/issues/47501

Further steps

Ideally we shouldn't have any JS logic about theme besides CSS classes set on the document element. And all colors should work through CSS variables *custom-properties, but at the moment, we have at least two major places where we have to have JS check about theme.

  • Brand logo (since we use svg as image, in img src attribute), we should change URL based on theme (in dark theme brand logo has difference color value). I'm wondering that we probably can change it and use svg directly without img tag)
  • Monaco editor doesn't support colors through CSS variables and we change set of colors in js runtime based on theme value, I don't know maybe with CodeMirror we could use just CSS variables and remove this logic

Test plan

  • Check web app with different theme values
  • Check jetbrains plugin
  • Check vscode webview extension
  • Check browser extension UI
  • Check code host integration tooltip UI

App preview:

Check out the client app preview documentation to learn more.

@sourcegraph-bot
Copy link
Contributor

sourcegraph-bot commented Feb 19, 2023

Codenotify: Notifying subscribers in CODENOTIFY files for diff 39488a949104ae63acfa867c794743d82e0429d2...4f4c4539672d2e38e0d9e600bc939660abdc4174.

Notify File(s)
@BolajiOlajide client/web/src/enterprise/batches/BatchSpec.tsx
client/web/src/enterprise/batches/BatchSpecNode.tsx
client/web/src/enterprise/batches/BatchSpecsPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/EditBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/editor/MonacoBatchSpecEditor.tsx
client/web/src/enterprise/batches/batch-spec/execute/ExecuteBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/execute/ReadOnlyBatchSpecForm.tsx
client/web/src/enterprise/batches/batch-spec/execute/workspaces/ExecutionWorkspaces.tsx
client/web/src/enterprise/batches/create/CreateBatchChangePage.tsx
client/web/src/enterprise/batches/detail/BatchChangeDetailsTabs.tsx
client/web/src/enterprise/batches/global/GlobalBatchChangesArea.tsx
client/web/src/enterprise/batches/preview/BatchChangePreviewTabs.tsx
client/web/src/enterprise/batches/repo/BatchChangeRepoPage.tsx
client/web/src/enterprise/batches/repo/RepositoryBatchChangesArea.tsx
@courier-new client/web/src/enterprise/batches/BatchSpec.tsx
client/web/src/enterprise/batches/BatchSpecNode.tsx
client/web/src/enterprise/batches/BatchSpecsPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/EditBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/editor/MonacoBatchSpecEditor.tsx
client/web/src/enterprise/batches/batch-spec/execute/ExecuteBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/execute/ReadOnlyBatchSpecForm.tsx
client/web/src/enterprise/batches/batch-spec/execute/workspaces/ExecutionWorkspaces.tsx
client/web/src/enterprise/batches/create/CreateBatchChangePage.tsx
client/web/src/enterprise/batches/detail/BatchChangeDetailsTabs.tsx
client/web/src/enterprise/batches/global/GlobalBatchChangesArea.tsx
client/web/src/enterprise/batches/preview/BatchChangePreviewTabs.tsx
client/web/src/enterprise/batches/repo/BatchChangeRepoPage.tsx
client/web/src/enterprise/batches/repo/RepositoryBatchChangesArea.tsx
@efritz client/web/src/enterprise/codeintel/configuration/components/ConfigurationEditor.tsx
client/web/src/enterprise/codeintel/configuration/components/InferenceScriptEditor.tsx
client/web/src/enterprise/codeintel/configuration/pages/CodeIntelConfigurationPage.tsx
client/web/src/enterprise/codeintel/configuration/pages/CodeIntelConfigurationPolicyPage.tsx
client/web/src/enterprise/codeintel/configuration/pages/CodeIntelRepositoryIndexConfigurationPage.tsx
client/web/src/enterprise/codeintel/indexes/pages/CodeIntelPreciseIndexPage.tsx
client/web/src/enterprise/codeintel/indexes/pages/CodeIntelPreciseIndexesPage.tsx
client/web/src/enterprise/codeintel/repo/RepositoryCodeIntelArea.tsx
@eseliger client/web/src/enterprise/batches/BatchSpec.tsx
client/web/src/enterprise/batches/BatchSpecNode.tsx
client/web/src/enterprise/batches/BatchSpecsPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/EditBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/edit/editor/MonacoBatchSpecEditor.tsx
client/web/src/enterprise/batches/batch-spec/execute/ExecuteBatchSpecPage.tsx
client/web/src/enterprise/batches/batch-spec/execute/ReadOnlyBatchSpecForm.tsx
client/web/src/enterprise/batches/batch-spec/execute/workspaces/ExecutionWorkspaces.tsx
client/web/src/enterprise/batches/create/CreateBatchChangePage.tsx
client/web/src/enterprise/batches/detail/BatchChangeDetailsTabs.tsx
client/web/src/enterprise/batches/global/GlobalBatchChangesArea.tsx
client/web/src/enterprise/batches/preview/BatchChangePreviewTabs.tsx
client/web/src/enterprise/batches/repo/BatchChangeRepoPage.tsx
client/web/src/enterprise/batches/repo/RepositoryBatchChangesArea.tsx
@fkling client/web/src/search/SearchConsolePage.tsx
client/web/src/search/home/SearchPage.story.tsx
client/web/src/search/home/SearchPage.tsx
client/web/src/search/home/SearchPageFooter.tsx
client/web/src/search/home/SearchPageInput.tsx
client/web/src/search/input/SearchNavbarItem.tsx
client/web/src/search/results/StreamingSearchResults.story.tsx
client/web/src/search/results/StreamingSearchResults.test.tsx
client/web/src/search/results/StreamingSearchResults.tsx
@limitedmage client/web/src/enterprise/code-monitoring/CodeMonitoringGettingStarted.tsx
client/web/src/enterprise/code-monitoring/CodeMonitoringPage.tsx
client/web/src/enterprise/code-monitoring/CreateCodeMonitorPage.tsx
client/web/src/enterprise/code-monitoring/ManageCodeMonitorPage.tsx
client/web/src/enterprise/code-monitoring/components/CodeMonitorForm.test.tsx
client/web/src/enterprise/code-monitoring/components/CodeMonitorForm.tsx
client/web/src/enterprise/code-monitoring/components/FormTriggerArea.test.tsx
client/web/src/enterprise/code-monitoring/components/FormTriggerArea.tsx
client/web/src/enterprise/code-monitoring/global/GlobalCodeMonitoringArea.tsx
client/web/src/search/SearchConsolePage.tsx
client/web/src/search/home/SearchPage.story.tsx
client/web/src/search/home/SearchPage.tsx
client/web/src/search/home/SearchPageFooter.tsx
client/web/src/search/home/SearchPageInput.tsx
client/web/src/search/input/SearchNavbarItem.tsx
client/web/src/search/results/StreamingSearchResults.story.tsx
client/web/src/search/results/StreamingSearchResults.test.tsx
client/web/src/search/results/StreamingSearchResults.tsx
@philipp-spiess client/jetbrains/webview/src/search/App.tsx
client/jetbrains/webview/src/search/input/JetBrainsSearchBox.story.tsx
client/jetbrains/webview/src/search/input/JetBrainsSearchBox.tsx
@vdavid client/jetbrains/webview/src/search/App.tsx
client/jetbrains/webview/src/search/input/JetBrainsSearchBox.story.tsx
client/jetbrains/webview/src/search/input/JetBrainsSearchBox.tsx

@sourcegraph-bot
Copy link
Contributor

sourcegraph-bot commented Feb 19, 2023

Codenotify: Notifying subscribers in OWNERS files for diff 39488a949104ae63acfa867c794743d82e0429d2...4f4c4539672d2e38e0d9e600bc939660abdc4174.

Notify File(s)
@sourcegraph/code-exploration-devs client/wildcard/src/stories/BrandedStory.tsx
client/wildcard/src/stories/hooks/index.ts
client/wildcard/src/stories/hooks/useStorybookTheme.ts
client/wildcard/src/stories/index.ts

@sg-e2e-regression-test-bob
Copy link

sg-e2e-regression-test-bob commented Feb 19, 2023

Bundle size report 📦

Initial size Total size Async size Modules
0.01% (+0.22 kb) -0.01% (-1.19 kb) -0.01% (-1.41 kb) 0.13% (+1) 🔺

Look at the Statoscope report for a full comparison between the commits f9ca312 and 63f7afb or learn more.

Open explanation
  • Initial size is the size of the initial bundle (the one that is loaded when you open the page)
  • Total size is the size of the initial bundle + all the async loaded chunks
  • Async size is the size of all the async loaded chunks
  • Modules is the number of modules in the initial bundle

@vovakulikov vovakulikov requested a review from fkling February 19, 2023 19:47
})
)
}
// if (codeHost.isLightTheme) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't need this anymore

@philipp-spiess
Copy link
Contributor

I’m going to do a more detailed review tomorrow but I want to comment and say how excited I am about this change. I have wished for a useTheme hook before: This is a very welcome change. 🙂

@github-actions
Copy link
Contributor

Problem: the label i-acknowledge-this-goes-into-the-release is absent.
👉 What to do: we're in the next Sourcegraph release code freeze period. If you are 100% sure your changes should get released or provide no risk to the release, add the label your PR with i-acknowledge-this-goes-into-the-release.
When does the freeze end? The code freeze is active until 2023-02-22 at 23:59 UTC.

Copy link
Contributor

@philipp-spiess philipp-spiess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oprah Crazy

autoFocus={true}
hideHelpButton={true}
/>
<ThemeContext.Provider value={{ themeSetting: !isDarkTheme ? ThemeSetting.Light : ThemeSetting.Dark }}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need a provider here if we have placed one already in client/jebtrains/webview/src/search/App.tsx

export function useTheme(): useThemeApi {
const { themeSetting: contextThemeSetting, onThemeSettingChanging } = useContext(ThemeContext)

const systemTheme = useSyncExternalStore(subscribeToSystemTheme, getSystemThemeSnapshot)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍 nice!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chef's kiss!

Comment on lines +40 to +44
useLayoutEffect(() => {
const isLightTheme = theme === Theme.Light

document.documentElement.classList.add('theme')
document.documentElement.classList.toggle('theme-light', isLightTheme)
document.documentElement.classList.toggle('theme-dark', !isLightTheme)
}, [theme])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we extract this into a hook as well? Seems like we need it in a few places

export const SiteAdminPingsPage: React.FunctionComponent<React.PropsWithChildren<Props>> = ({ isLightTheme }) => {
export const SiteAdminPingsPage: React.FunctionComponent<React.PropsWithChildren<Props>> = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After removing most of the prop drilling, I imagine this is how most of the components will look. No props!

Comment on lines 61 to 64
isLightTheme={true}
onThemePreferenceChange={() => undefined}
showKeyboardShortcutsHelp={() => undefined}
themePreference={ThemePreference.Light}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌪️

Copy link
Member

@valerybugakov valerybugakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is huge, @vovakulikov! Thank you for looking into it 🔥

@vovakulikov
Copy link
Contributor Author

@valerybugakov @philipp-spiess Ideally, I want to generalise common core/infra state with context approach. For example, at the moment, we have some infrorations and services on the top level that many consumers within want to know about, like auth user, telemetry service, setting cascade, ...etc.

And some of these global info stores were migrated from props (or partially migrated); for example, we have a special global store/hook for the experimental features. BUT i think having separate stores-hooks isn't a best idea here because you have to know about them to use it. Imagine if we shared it with one hook/context where we can fine all that we may need (and we had suggestions there)

const { experimentalFeatures, theme, telemetryServices, ... } = useCore()

Having one place to read core info implies that we have to create a separate package for it, and this is what stopped me from doing it here in this PR (it's just too big for one PR, and actually, there is a problem from decoupling temporary setting hook), If you have any ideas about it, please let me know.

@philipp-spiess
Copy link
Contributor

BUT i think having separate stores-hooks isn't a best idea here because you have to know about them to use it. Imagine if we shared it with one hook/context where we can fine all that we may need (and we had suggestions there)

Hmmm I think I personally would prefer individual hooks for all of these because they don't really share logic and some of these (e.g. theme) might be updated where as others (e.g. telemetry stuff) are never changed which could cause unnecessary re-renderings when we have to subscribe to all context values.

But yeah everything is better than the current prop-drilling 😅

@vovakulikov
Copy link
Contributor Author

@valerybugakov, it seems like there is a problem with vscode extension. I couldn't manage to run it locally due to problem with depencencie resolver, and I noticed that there are two deps that are not resolved

 "@vscode/codicons": "^0.0.29",
 "@vscode/webview-ui-toolkit": "^0.9.0",

but then "@microsoft/fast-web-utilities": "5.1.0" also is not found (I tried to add it to deps, but this didn't help), If you got a minute can you take a look (apparently nobody uses this locally anymore I think, this is why we didn't catch thing during pnpm migration)

@vovakulikov
Copy link
Contributor Author

Ok I think I got deps problem, so I manged to build vscode extension
we have to add two dependencies explicitly on the top level

  "@microsoft/fast-web-utilities": "5.1.0",
  "@microsoft/fast-element": "1.7.0",

I think this is the ghost deps package problem, and this used to work before only because of how yarn and node deps resolution works (and how it doesn't work now with pnpm resolvers)

But then when I try to debug this locally I see blank screen (no runtime errors found in the terminal), does anyone know here what it may be?

Screen.Recording.2023-02-20.at.21.08.50.mov

@vovakulikov
Copy link
Contributor Author

A few thoughts on jetbrains plugin

  • It seems that esbuild (which is used by default for pnpm watch is failing with Markdown CSS (and this script just doesn't work)
  • pnpm standalone produces localhost with non working version of search UI (see screenshot below)

Screenshot 2023-02-20 at 22 18 51

- And unfortunately at the end I couldn't run the plugin in sandboxes because java 11 isn't compatible with java version of IDE tookit.

@philipp-spiess I remember you used to work with JB plugins, let me know if you have a minute to guide me through the standard process (maybe there is some catch with version that I'm missing)

@vovakulikov
Copy link
Contributor Author

I managed to test code host integrations and browser extensions though, so it seems that after vscode and jb plugins we're good to go here

@vovakulikov vovakulikov force-pushed the vk/improve-theme-state branch from 9a8e581 to 4f4c453 Compare February 21, 2023 01:47
@philipp-spiess
Copy link
Contributor

@vovakulikov

Regarding VS Code: No I don't remember seeing the blank screen before 😞 I also don't recall having had an issue with dependencies, @taras-yemets and me have made a change in vscode not too long ago!

Regarding JetBrains: I can't get it to run myself either. It's crashing with this issue now 🤔

Screenshot 2023-02-21 at 14 52 23

This seems unrelated to your change though. I wonder what goes on. I'll need more time to look into this I’m afraid 😭

@philipp-spiess
Copy link
Contributor

@vovakulikov It does look like the build script for JetBrains is broken, yeah. I don't see the dist folder being created

@vovakulikov
Copy link
Contributor Author

vovakulikov commented Feb 21, 2023

Here is a crazy idea (maybe not so bad actually if nobody has any things against it). What if we just forget about vscode and jetbrains plugins in this PR? I know that these two are not a priority at the moment (in fact, I haven't seen any big/or any changes in these plugins for a long time)

So I propose to merge this PR and deal with possible regressions about theme in VSCode and JB plugin when we fix the vscode and jetbrains plugins). I would like to fix them, but at the moment, I don't have capacity even to dive deep there (maybe it's one liner fix, who knows).

cc @valerybugakov @philipp-spiess

@philipp-spiess
Copy link
Contributor

Here is a crazy idea (maybe not so bad actually if nobody has any things against it). What if we just forget about vscode and jetbrains plugins in this PR? I know that these two are not a priority at the moment (in fact, I haven't seen any big/or any changes in these plugins for a long time)

So I propose to merge this PR and deal with possible regressions about theme in VSCode and JB plugin when we fix the vscode and jetbrains plugins). I would like to fix them, but at the moment, I don't have capacity even to dive deep there (maybe it's one liner fix, who knows).

This sounds reasonable to me, it's very expensive to keep this branch open and we can't miss out on the benefits it brings. I would also add that a regression because of the themes is not very likely given that this is not a high risk change. WDYT @taras-yemets?

Copy link
Member

@valerybugakov valerybugakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently, nobody uses this locally anymore, I think; this is why we didn't catch thing during pnpm migration

Eh, that's alarming. We should add this build to our CI pipeline! I created an issue for that: https://github.com/sourcegraph/sourcegraph/issues/48012

Agree with the proposal to merge this PR as is and create a follow-up issue to verify that the JetBrains extension works as expected later once we fix the build.

Copy link
Contributor

@vdavid vdavid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, very nice! I only read the desc and scrolled through the changes, but it's very promising to make our lives easier on the front end! Thanks Vova! ❤️

philipp-spiess added a commit that referenced this pull request Feb 22, 2023
Inspired by Vova's #47864, this adds a new `useSettings()` hook to
replace the existing `SettingsCascadeProps` prop drilling.

I decided to _not_ replace all of the props drilling just yet to avoid a
massive merge conflict when Vova's PR ships. I also really want to focus
on cleaning up the app shell instead and I need this particularly for
one use case: Cleaning up `globbing` which, as it turns out, is just a
computed value from the setting instead.

With this new settings API in place, I want to start trimming the shared
props mess in a follow-up PR. This will then already remove the needs to
pass the settings cascade around naturally and we will end up with a few
smaller issues instead of a high change of breaking things.

I've also marked the existing settings APIs (including access to the
`SettingsCascade` object directly) as deprecated. `useSettings()` should
be used when possible. I don't think we need to validate the settings in
every caller manually.
@vovakulikov vovakulikov force-pushed the vk/improve-theme-state branch from 6f3f261 to b6dff1f Compare February 22, 2023 15:55
@vovakulikov vovakulikov force-pushed the vk/improve-theme-state branch from b6dff1f to f9ca312 Compare February 22, 2023 16:00
@vovakulikov
Copy link
Contributor Author

Ok, I'm about to merge this because I already got two big conflicts with main, so if something will show up because of this PR let me know

@vovakulikov vovakulikov merged commit ce8679b into main Feb 22, 2023
@vovakulikov vovakulikov deleted the vk/improve-theme-state branch February 22, 2023 16:28
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants