mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-05 07:09:02 +02:00
Merge pull request #29152 from overleaf/dp-dashboard-dark-mode
Add data-theme attribute to project list page GitOrigin-RevId: 3a623e3258d55e01f0911bcc45b78bcdba21745b
This commit is contained in:
@@ -50,6 +50,7 @@ import UserGetter from '../User/UserGetter.js'
|
||||
import { isStandaloneAiAddOnPlanCode } from '../Subscription/AiHelper.js'
|
||||
import SubscriptionController from '../Subscription/SubscriptionController.mjs'
|
||||
import { formatCurrency } from '../../util/currency.js'
|
||||
import UserSettingsHelper from './UserSettingsHelper.mjs'
|
||||
|
||||
const { ObjectId } = mongodb
|
||||
/**
|
||||
@@ -811,22 +812,7 @@ const _ProjectController = {
|
||||
isMemberOfGroupSubscription: userIsMemberOfGroupSubscription,
|
||||
hasInstitutionLicence: userHasInstitutionLicence,
|
||||
},
|
||||
userSettings: {
|
||||
mode: user.ace.mode,
|
||||
editorTheme: user.ace.theme,
|
||||
fontSize: user.ace.fontSize,
|
||||
autoComplete: user.ace.autoComplete,
|
||||
autoPairDelimiters: user.ace.autoPairDelimiters,
|
||||
pdfViewer: user.ace.pdfViewer,
|
||||
syntaxValidation: user.ace.syntaxValidation,
|
||||
fontFamily: user.ace.fontFamily || 'lucida',
|
||||
lineHeight: user.ace.lineHeight || 'normal',
|
||||
overallTheme: user.ace.overallTheme,
|
||||
mathPreview: user.ace.mathPreview,
|
||||
breadcrumbs: user.ace.breadcrumbs,
|
||||
referencesSearchMode: user.ace.referencesSearchMode,
|
||||
enableNewEditor: user.ace.enableNewEditor ?? true,
|
||||
},
|
||||
userSettings: UserSettingsHelper.buildUserSettings(user),
|
||||
labsExperiments: user.labsExperiments ?? [],
|
||||
privilegeLevel,
|
||||
anonymous,
|
||||
|
||||
@@ -31,6 +31,7 @@ import SubscriptionHelper from '../Subscription/SubscriptionHelper.js'
|
||||
import PermissionsManager from '../Authorization/PermissionsManager.mjs'
|
||||
import AnalyticsManager from '../Analytics/AnalyticsManager.js'
|
||||
import { OnboardingDataCollection } from '../../models/OnboardingDataCollection.js'
|
||||
import UserSettingsHelper from './UserSettingsHelper.mjs'
|
||||
|
||||
/**
|
||||
* @import { GetProjectsRequest, GetProjectsResponse, AllUsersProjects, MongoProject, FormattedProject, MongoTag } from "./types"
|
||||
@@ -164,7 +165,7 @@ async function projectListPage(req, res, next) {
|
||||
})
|
||||
const user = await User.findById(
|
||||
userId,
|
||||
`email emails features alphaProgram betaProgram lastPrimaryEmailCheck lastActive signUpDate refProviders${
|
||||
`email emails features alphaProgram betaProgram lastPrimaryEmailCheck lastActive signUpDate ace refProviders${
|
||||
isSaas ? ' enrollment writefull completedTutorials aiErrorAssistant' : ''
|
||||
}`
|
||||
)
|
||||
@@ -537,6 +538,12 @@ async function projectListPage(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
await SplitTestHandler.promises.getAssignment(
|
||||
req,
|
||||
res,
|
||||
'themed-project-dashboard'
|
||||
)
|
||||
|
||||
res.render('project/list-react', {
|
||||
title: 'your_projects',
|
||||
usersBestSubscription,
|
||||
@@ -545,6 +552,7 @@ async function projectListPage(req, res, next) {
|
||||
user,
|
||||
userAffiliations,
|
||||
userEmails,
|
||||
userSettings: UserSettingsHelper.buildUserSettings(user),
|
||||
reconfirmedViaSAML,
|
||||
allInReconfirmNotificationPeriods,
|
||||
survey,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
function buildUserSettings(user) {
|
||||
return {
|
||||
mode: user.ace.mode,
|
||||
editorTheme: user.ace.theme,
|
||||
fontSize: user.ace.fontSize,
|
||||
autoComplete: user.ace.autoComplete,
|
||||
autoPairDelimiters: user.ace.autoPairDelimiters,
|
||||
pdfViewer: user.ace.pdfViewer,
|
||||
syntaxValidation: user.ace.syntaxValidation,
|
||||
fontFamily: user.ace.fontFamily || 'lucida',
|
||||
lineHeight: user.ace.lineHeight || 'normal',
|
||||
overallTheme: user.ace.overallTheme,
|
||||
mathPreview: user.ace.mathPreview,
|
||||
breadcrumbs: user.ace.breadcrumbs,
|
||||
referencesSearchMode: user.ace.referencesSearchMode,
|
||||
enableNewEditor: user.ace.enableNewEditor ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
buildUserSettings,
|
||||
}
|
||||
@@ -33,6 +33,7 @@ block append meta
|
||||
meta(name='ol-survey' data-type='json' content=survey)
|
||||
meta(name='ol-tags' data-type='json' content=tags)
|
||||
meta(name='ol-portalTemplates' data-type='json' content=portalTemplates)
|
||||
meta(name='ol-userSettings' data-type='json' content=userSettings)
|
||||
meta(
|
||||
name='ol-prefetchedProjectsBlob'
|
||||
data-type='json'
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import _ from 'lodash'
|
||||
import { saveUserSettings } from '../utils/api'
|
||||
import { UserSettings } from '../../../../../types/user-settings'
|
||||
import { useUserSettingsContext } from '@/shared/context/user-settings-context'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme'
|
||||
|
||||
export default function useSetOverallTheme() {
|
||||
const { userSettings, setUserSettings } = useUserSettingsContext()
|
||||
@@ -16,13 +15,6 @@ export default function useSetOverallTheme() {
|
||||
},
|
||||
[setUserSettings]
|
||||
)
|
||||
const activeOverallTheme = useActiveOverallTheme()
|
||||
|
||||
useEffect(() => {
|
||||
// Sets the body's data-theme attribute for theming
|
||||
document.body.dataset.theme =
|
||||
activeOverallTheme === 'dark' ? 'default' : 'light'
|
||||
}, [activeOverallTheme])
|
||||
|
||||
return useCallback(
|
||||
(newOverallTheme: UserSettings['overallTheme']) => {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import EditorSurvey from '../editor-survey'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import { useStatusFavicon } from '@/features/ide-react/hooks/use-status-favicon'
|
||||
import useThemedPage from '@/shared/hooks/use-themed-page'
|
||||
|
||||
const MainLayoutNew = lazy(
|
||||
() => import('@/features/ide-redesign/components/main-layout')
|
||||
@@ -32,6 +33,7 @@ export default function IdePage() {
|
||||
useRegisterUserActivity() // record activity and ensure connection when user is active
|
||||
useHasLintingError() // pass editor:lint hasLintingError to the compiler
|
||||
useStatusFavicon() // update the favicon based on the compile status
|
||||
useThemedPage() // set the page theme based on user settings
|
||||
|
||||
const newEditor = useIsNewEditorEnabled()
|
||||
const canAccessNewEditor = canUseNewEditor()
|
||||
|
||||
@@ -76,9 +76,9 @@ export const ReactContextRoot: FC<
|
||||
<Providers.ModalsContextProvider>
|
||||
<Providers.ConnectionProvider>
|
||||
<Providers.ProjectProvider>
|
||||
<Providers.IdeReactProvider>
|
||||
<Providers.UserProvider>
|
||||
<Providers.UserSettingsProvider>
|
||||
<Providers.UserSettingsProvider>
|
||||
<Providers.IdeReactProvider>
|
||||
<Providers.UserProvider>
|
||||
<Providers.SnapshotProvider>
|
||||
<Providers.DetachProvider>
|
||||
<Providers.EditorPropertiesProvider>
|
||||
@@ -128,9 +128,9 @@ export const ReactContextRoot: FC<
|
||||
</Providers.EditorPropertiesProvider>
|
||||
</Providers.DetachProvider>
|
||||
</Providers.SnapshotProvider>
|
||||
</Providers.UserSettingsProvider>
|
||||
</Providers.UserProvider>
|
||||
</Providers.IdeReactProvider>
|
||||
</Providers.UserProvider>
|
||||
</Providers.IdeReactProvider>
|
||||
</Providers.UserSettingsProvider>
|
||||
</Providers.ProjectProvider>
|
||||
</Providers.ConnectionProvider>
|
||||
</Providers.ModalsContextProvider>
|
||||
|
||||
@@ -19,6 +19,8 @@ import WelcomePageContent from '@/features/project-list/components/welcome-page-
|
||||
import { ProjectListDsNav } from '@/features/project-list/components/project-list-ds-nav'
|
||||
import { DsNavStyleProvider } from '@/features/project-list/components/use-is-ds-nav'
|
||||
import CookieBanner from '@/shared/components/cookie-banner'
|
||||
import useThemedPage from '@/shared/hooks/use-themed-page'
|
||||
import { UserSettingsProvider } from '@/shared/context/user-settings-context'
|
||||
|
||||
function ProjectListRoot() {
|
||||
const { isReady } = useWaitForI18n()
|
||||
@@ -35,7 +37,9 @@ export function ProjectListRootInner() {
|
||||
<ProjectListProvider>
|
||||
<ColorPickerProvider>
|
||||
<SplitTestProvider>
|
||||
<ProjectListPageContent />
|
||||
<UserSettingsProvider>
|
||||
<ProjectListPageContent />
|
||||
</UserSettingsProvider>
|
||||
</SplitTestProvider>
|
||||
</ColorPickerProvider>
|
||||
</ProjectListProvider>
|
||||
@@ -70,6 +74,7 @@ function DefaultPageContentWrapper({ children }: { children: ReactNode }) {
|
||||
}
|
||||
|
||||
function ProjectListPageContent() {
|
||||
useThemedPage('themed-project-dashboard')
|
||||
const { totalProjectsCount, isLoading, loadProgress } =
|
||||
useProjectListContext()
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import { createContext, FC, useContext, useEffect, useMemo } from 'react'
|
||||
import { ScopeValueStore } from '../../../../types/ide/scope-value-store'
|
||||
import { ScopeEventEmitter } from '../../../../types/ide/scope-event-emitter'
|
||||
import { Socket } from '@/features/ide-react/connection/types/socket'
|
||||
import { useUserSettingsContext } from './user-settings-context'
|
||||
import { userStyles } from '../utils/styles'
|
||||
import { canUseNewEditor } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import { useActiveOverallTheme } from '../hooks/use-active-overall-theme'
|
||||
|
||||
export type Ide = {
|
||||
socket: Socket
|
||||
@@ -44,6 +48,21 @@ export const IdeProvider: FC<
|
||||
}
|
||||
}, [unstableStore])
|
||||
|
||||
const { userSettings } = useUserSettingsContext()
|
||||
const activeOverallTheme = useActiveOverallTheme()
|
||||
|
||||
useEffect(() => {
|
||||
const { fontFamily, lineHeight } = userStyles(userSettings)
|
||||
unstableStore.set('settings', {
|
||||
overallTheme: activeOverallTheme,
|
||||
keybindings: userSettings.mode === 'none' ? 'default' : userSettings.mode,
|
||||
fontFamily,
|
||||
lineHeight,
|
||||
fontSize: userSettings.fontSize,
|
||||
isNewEditor: canUseNewEditor() && userSettings.enableNewEditor,
|
||||
})
|
||||
}, [unstableStore, userSettings, activeOverallTheme])
|
||||
|
||||
const value = useMemo<IdeContextValue>(() => {
|
||||
return {
|
||||
...ide,
|
||||
|
||||
@@ -6,14 +6,9 @@ import {
|
||||
SetStateAction,
|
||||
FC,
|
||||
useState,
|
||||
useEffect,
|
||||
} from 'react'
|
||||
|
||||
import { UserSettings } from '../../../../types/user-settings'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { userStyles } from '../utils/styles'
|
||||
import { canUseNewEditor } from '@/features/ide-redesign/utils/new-editor-utils'
|
||||
import { useIdeContext } from '@/shared/context/ide-context'
|
||||
|
||||
const defaultSettings: UserSettings = {
|
||||
pdfViewer: 'pdfjs',
|
||||
@@ -50,20 +45,6 @@ export const UserSettingsProvider: FC<React.PropsWithChildren> = ({
|
||||
() => getMeta('ol-userSettings') || defaultSettings
|
||||
)
|
||||
|
||||
// update the global scope 'settings' value, for extensions
|
||||
const { unstableStore } = useIdeContext()
|
||||
useEffect(() => {
|
||||
const { fontFamily, lineHeight } = userStyles(userSettings)
|
||||
unstableStore.set('settings', {
|
||||
overallTheme: userSettings.overallTheme === 'light-' ? 'light' : 'dark',
|
||||
keybindings: userSettings.mode === 'none' ? 'default' : userSettings.mode,
|
||||
fontFamily,
|
||||
lineHeight,
|
||||
fontSize: userSettings.fontSize,
|
||||
isNewEditor: canUseNewEditor() && userSettings.enableNewEditor,
|
||||
})
|
||||
}, [unstableStore, userSettings])
|
||||
|
||||
const value = useMemo<UserSettingsContextValue>(
|
||||
() => ({
|
||||
userSettings,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useActiveOverallTheme } from './use-active-overall-theme'
|
||||
import { useSplitTestContext } from '../context/split-test-context'
|
||||
|
||||
export default function useThemedPage(featureFlag?: string) {
|
||||
const { splitTestVariants } = useSplitTestContext()
|
||||
|
||||
let activeOverallTheme = useActiveOverallTheme()
|
||||
|
||||
// Override theme if feature flag is provided and not enabled
|
||||
if (featureFlag && splitTestVariants[featureFlag] !== 'enabled') {
|
||||
activeOverallTheme = 'light'
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Sets the body's data-theme attribute for theming
|
||||
document.body.dataset.theme =
|
||||
activeOverallTheme === 'dark' ? 'default' : 'light'
|
||||
}, [activeOverallTheme])
|
||||
}
|
||||
@@ -23,6 +23,16 @@ describe('ProjectListController', function () {
|
||||
lastActive: new Date(2),
|
||||
signUpDate: new Date(1),
|
||||
lastLoginIp: '111.111.111.112',
|
||||
ace: {
|
||||
syntaxValidation: true,
|
||||
pdfViewer: 'pdfjs',
|
||||
spellCheckLanguage: 'en',
|
||||
autoPairDelimiters: true,
|
||||
autoComplete: true,
|
||||
fontSize: 12,
|
||||
theme: 'textmate',
|
||||
mode: 'none',
|
||||
},
|
||||
}
|
||||
ctx.users = {
|
||||
'user-1': {
|
||||
|
||||
Reference in New Issue
Block a user