mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-05 22:38:48 +00:00
Merge pull request #12936 from ToolJet/gh-12680-toggle-app-mode
Add action to toggle app mode (light/dark)
This commit is contained in:
commit
f1f9a57e3d
14 changed files with 126 additions and 24 deletions
|
|
@ -88,6 +88,7 @@ export function getSuggestionKeys(refState) {
|
|||
'unsetPageVariable',
|
||||
'unsetAllPageVariables',
|
||||
'switchPage',
|
||||
'toggleAppMode',
|
||||
];
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import useAppDarkMode from '@/_hooks/useAppDarkMode';
|
|||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
const APP_MODES = [
|
||||
export const APP_MODES = [
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
{ label: 'Light', value: 'light' },
|
||||
{ label: 'Dark', value: 'dark' },
|
||||
|
|
@ -15,12 +15,7 @@ const APP_MODES = [
|
|||
const AppModeToggle = ({ darkMode }) => {
|
||||
const { onAppModeChange, appMode } = useAppDarkMode();
|
||||
const { t } = useTranslation();
|
||||
const { globalSettingsChanged } = useStore(
|
||||
(state) => ({
|
||||
globalSettingsChanged: state.globalSettingsChanged,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
const setResolvedGlobals = useStore((state) => state.setResolvedGlobals);
|
||||
|
||||
return (
|
||||
|
|
@ -33,8 +28,7 @@ const AppModeToggle = ({ darkMode }) => {
|
|||
if (value === 'auto') {
|
||||
exposedTheme = darkMode ? 'dark' : 'light';
|
||||
}
|
||||
onAppModeChange({ appMode: value });
|
||||
// globalSettingsChanged({ theme: { name: exposedTheme } });
|
||||
onAppModeChange(value);
|
||||
setResolvedGlobals('theme', { name: exposedTheme });
|
||||
}}
|
||||
defaultValue={appMode}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import ToggleGroupItem from '@/ToolJetUI/SwitchGroup/ToggleGroupItem';
|
|||
import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { components as selectComponents } from 'react-select';
|
||||
import { APP_MODES } from '@/AppBuilder/LeftSidebar/GlobalSettings/AppModeToggle';
|
||||
|
||||
export const EventManager = ({
|
||||
sourceId,
|
||||
|
|
@ -75,7 +76,6 @@ export const EventManager = ({
|
|||
const eventToDeleteLoaderIndex = useStore((state) => state.eventsSlice.getEventToDeleteLoaderIndex(), shallow);
|
||||
|
||||
const { handleYmapEventUpdates } = useContext(EditorContext) || {};
|
||||
|
||||
const { updateState } = useAppDataActions();
|
||||
|
||||
const currentEvents = allAppEvents?.filter((event) => {
|
||||
|
|
@ -1044,6 +1044,29 @@ export const EventManager = ({
|
|||
})}
|
||||
</>
|
||||
)}
|
||||
{event.actionId === 'toggle-app-mode' && (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.appMode', 'App mode')}</div>
|
||||
<div className="col-9" data-cy="query-selection-field">
|
||||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'} w-100`}
|
||||
options={APP_MODES}
|
||||
value={event?.appMode}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
handlerChanged(index, 'appMode', value);
|
||||
}}
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
useMenuPortal={false}
|
||||
useCustomStyles={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<RunjsParameters event={event} darkMode={darkMode} index={index} handlerChanged={handlerChanged} />
|
||||
</>
|
||||
)}
|
||||
<div className="row mt-3">
|
||||
<div className="col-3 p-2">{t('editor.inspector.eventManager.debounce', 'Debounce')}</div>
|
||||
<div className="col-9">
|
||||
|
|
|
|||
|
|
@ -334,11 +334,10 @@ const useAppData = (
|
|||
appData.editing_version?.homePageId || appData.editing_version?.home_page_id || appData.home_page_id;
|
||||
|
||||
appTypeRef.current = appData.type;
|
||||
|
||||
setApp(
|
||||
{
|
||||
appName: appData.name,
|
||||
appId: appData.id,
|
||||
appId: appId || appData?.appId || appData?.app_id,
|
||||
slug: appData.slug,
|
||||
currentAppEnvironmentId: editorEnvironment.id,
|
||||
isMaintenanceOn:
|
||||
|
|
|
|||
|
|
@ -172,6 +172,20 @@ export const createAppSlice = (set, get) => ({
|
|||
console.error('Error updating page:', error);
|
||||
}
|
||||
},
|
||||
updateAppMode: async (appMode, moduleId = 'canvas') => {
|
||||
const { appStore, currentVersionId } = get();
|
||||
try {
|
||||
const res = await appVersionService.updateAppMode(
|
||||
appStore.modules[moduleId].app.appId,
|
||||
currentVersionId,
|
||||
appMode
|
||||
);
|
||||
set((state) => ({ globalSettings: { ...state.globalSettings, appMode } }));
|
||||
} catch (error) {
|
||||
toast.error('App mode could not be updated.');
|
||||
console.error('Error updating app mode:', error);
|
||||
}
|
||||
},
|
||||
switchPage: (pageId, handle, queryParams = [], moduleId = 'canvas', isBackOrForward = false) => {
|
||||
get().debugger.resetUnreadErrorCount();
|
||||
// reset stores
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { appVersionService } from '@/_services';
|
||||
import toast from 'react-hot-toast';
|
||||
import { findAllEntityReferences } from '@/_stores/utils';
|
||||
import { debounce, extractAndReplaceReferencesFromString, resolveCode, replaceEntityReferencesWithIds } from '../utils';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
import { dfs } from '@/_stores/handleReferenceTransactions';
|
||||
import { debounce, replaceEntityReferencesWithIds } from '../utils';
|
||||
import { isQueryRunnable, isValidUUID, serializeNestedObjectToQueryParams } from '@/_helpers/utils';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import _ from 'lodash';
|
||||
|
|
@ -874,6 +872,11 @@ export const createEventsSlice = (set, get) => ({
|
|||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
case 'toggle-app-mode': {
|
||||
const { updateAppMode } = get();
|
||||
updateAppMode(event.appMode);
|
||||
return Promise.resolve();
|
||||
}
|
||||
case 'switch-page': {
|
||||
try {
|
||||
const { pageId } = event;
|
||||
|
|
@ -928,7 +931,14 @@ export const createEventsSlice = (set, get) => ({
|
|||
}),
|
||||
|
||||
generateAppActions: (queryId, mode, isPreview = false, moduleId = 'canvas') => {
|
||||
const { getCurrentPageComponents, dataQuery, eventsSlice, queryPanel, modules } = get();
|
||||
const {
|
||||
getCurrentPageComponents,
|
||||
dataQuery,
|
||||
eventsSlice,
|
||||
queryPanel,
|
||||
modules,
|
||||
globalSettings: { appMode },
|
||||
} = get();
|
||||
const { previewQuery } = queryPanel;
|
||||
const { executeAction } = eventsSlice;
|
||||
const currentComponents = Object.entries(getCurrentPageComponents(moduleId));
|
||||
|
|
@ -1202,6 +1212,20 @@ export const createEventsSlice = (set, get) => ({
|
|||
return executeAction(event, mode, {}, moduleId);
|
||||
};
|
||||
|
||||
const toggleAppMode = (value) => {
|
||||
if (value && value !== 'light' && value !== 'dark' && value !== 'auto') {
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
value = appMode === 'dark' ? 'light' : 'dark';
|
||||
}
|
||||
const event = {
|
||||
actionId: 'toggle-app-mode',
|
||||
appMode: value,
|
||||
};
|
||||
return executeAction(event, mode, {});
|
||||
};
|
||||
|
||||
return {
|
||||
runQuery,
|
||||
setVariable,
|
||||
|
|
@ -1224,6 +1248,7 @@ export const createEventsSlice = (set, get) => ({
|
|||
logInfo,
|
||||
log,
|
||||
logError,
|
||||
toggleAppMode,
|
||||
};
|
||||
},
|
||||
// Selectors
|
||||
|
|
|
|||
|
|
@ -492,6 +492,7 @@ export function createReferencesLookup(currentState, forQueryParams = false, ini
|
|||
'logInfo',
|
||||
'log',
|
||||
'logError',
|
||||
'toggleAppMode',
|
||||
];
|
||||
|
||||
const suggestionList = [];
|
||||
|
|
|
|||
|
|
@ -139,4 +139,10 @@ export const ActionTypes = [
|
|||
options: [{ name: 'copy-to-clipboard', type: 'text', default: '' }],
|
||||
group: 'other',
|
||||
},
|
||||
{
|
||||
name: 'Toggle app mode',
|
||||
id: 'toggle-app-mode',
|
||||
options: [{ name: 'appMode', type: 'text', default: '' }],
|
||||
group: 'other',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { shallow } from 'zustand/shallow';
|
|||
import useStore from '@/AppBuilder/_stores/store';
|
||||
|
||||
const useAppDarkMode = () => {
|
||||
const { appMode, globalSettingsChanged, isTJDarkMode } = useStore(
|
||||
const { appMode, updateAppMode, isTJDarkMode } = useStore(
|
||||
(state) => ({
|
||||
appMode: state.globalSettings.appMode,
|
||||
globalSettingsChanged: state.globalSettingsChanged,
|
||||
updateAppMode: state.updateAppMode,
|
||||
isTJDarkMode: state.isTJDarkMode,
|
||||
}),
|
||||
shallow
|
||||
|
|
@ -23,7 +23,7 @@ const useAppDarkMode = () => {
|
|||
}, [appMode, isTJDarkMode]);
|
||||
|
||||
return {
|
||||
onAppModeChange: globalSettingsChanged,
|
||||
onAppModeChange: updateAppMode,
|
||||
appMode,
|
||||
isAppDarkMode,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export const appVersionService = {
|
|||
deleteAppVersionEventHandler,
|
||||
clonePage,
|
||||
findAllEventsWithSourceId,
|
||||
updateAppMode,
|
||||
cloneGroup,
|
||||
};
|
||||
|
||||
|
|
@ -39,7 +40,9 @@ function promoteEnvironment(appId, versionId, currentEnvironmentId) {
|
|||
}
|
||||
function getAppVersionData(appId, versionId, mode) {
|
||||
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
|
||||
return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}?mode=${mode}`, requestOptions).then(handleResponse);
|
||||
return fetch(`${config.apiUrl}/v2/apps/${appId}/versions/${versionId}?mode=${mode}`, requestOptions).then(
|
||||
handleResponse
|
||||
);
|
||||
}
|
||||
|
||||
function create(appId, versionName, versionFromId, currentEnvironmentId) {
|
||||
|
|
@ -140,12 +143,23 @@ function autoSaveApp(
|
|||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
const url = `${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/${type ?? ''}`;
|
||||
|
||||
return fetch(url, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function updateAppMode(appId, versionId, appMode) {
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ appId, versionId, appMode }),
|
||||
};
|
||||
const url = `${config.apiUrl}/v2/apps/${appId}/versions/${versionId}/global_settings/app_mode`;
|
||||
|
||||
return fetch(url, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function saveAppVersionEventHandlers(appId, versionId, events, updateType = 'update') {
|
||||
const body = {
|
||||
events,
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ export class AppsService implements IAppsService {
|
|||
globalSettings: { ...versionToLoad.globalSettings, theme: appTheme },
|
||||
showViewerNavigation: versionToLoad.showViewerNavigation,
|
||||
pageSettings: versionToLoad?.pageSettings,
|
||||
appId: app.id,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Get, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Put, Param, Query, UseGuards } from '@nestjs/common';
|
||||
import { VersionService } from './service';
|
||||
import { InitModule } from '@modules/app/decorators/init-module';
|
||||
import { MODULES } from '@modules/app/constants/modules';
|
||||
|
|
@ -21,7 +21,7 @@ import { IVersionControllerV2 } from './interfaces/IControllerV2';
|
|||
version: '2',
|
||||
})
|
||||
export class VersionControllerV2 implements IVersionControllerV2 {
|
||||
constructor(protected readonly versionService: VersionService) {}
|
||||
constructor(protected readonly versionService: VersionService) { }
|
||||
|
||||
@InitFeature(FEATURE_KEY.GET_ONE)
|
||||
@UseGuards(JwtAuthGuard, ValidAppGuard, FeatureAbilityGuard)
|
||||
|
|
@ -37,6 +37,13 @@ export class VersionControllerV2 implements IVersionControllerV2 {
|
|||
return this.versionService.update(app, user, appVersionUpdateDto);
|
||||
}
|
||||
|
||||
// If we want to update app mode in public app, we use this endpoint
|
||||
@InitFeature(FEATURE_KEY.UPDATE_SETTINGS)
|
||||
@Put(':id/versions/:versionId/global_settings/app_mode')
|
||||
updateAppMode(@Body() @Param('appMode') appMode: 'light' | 'dark' | 'auto', @Param('id') appId: string, @Param('versionId') versionId: string) {
|
||||
return this.versionService.updateAppMode(appId, versionId, appMode);
|
||||
}
|
||||
|
||||
@InitFeature(FEATURE_KEY.UPDATE_SETTINGS)
|
||||
@UseGuards(JwtAuthGuard, ValidAppGuard, FeatureAbilityGuard)
|
||||
@Put([':id/versions/:versionId/global_settings', ':id/versions/:versionId/page_settings'])
|
||||
|
|
@ -48,6 +55,8 @@ export class VersionControllerV2 implements IVersionControllerV2 {
|
|||
return this.versionService.updateSettings(app, user, appVersionUpdateDto);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@InitFeature(FEATURE_KEY.PROMOTE)
|
||||
@UseGuards(JwtAuthGuard, ValidAppGuard, FeatureAbilityGuard)
|
||||
@Put(':id/versions/:versionId/promote')
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@ export interface IVersionControllerV2 {
|
|||
getVersion(user: UserEntity, app: AppEntity, mode?: string): Promise<any>;
|
||||
updateVersion(user: UserEntity, app: AppEntity, appVersionUpdateDto: AppVersionUpdateDto): Promise<any>;
|
||||
updateGlobalSettings(user: UserEntity, app: AppEntity, appVersionUpdateDto: AppVersionUpdateDto): Promise<any>;
|
||||
updateAppMode(appId: string, versionId: string, appMode: 'light' | 'dark' | 'auto'): Promise<any>;
|
||||
promoteVersion(user: UserEntity, app: AppEntity, promoteVersionDto: PromoteVersionDto): Promise<any>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ export class VersionService implements IVersionService {
|
|||
|
||||
async updateSettings(app: App, user: User, appVersionUpdateDto: AppVersionUpdateDto) {
|
||||
const appVersion = await this.versionRepository.findById(app.appVersions[0].id, app.id);
|
||||
|
||||
|
||||
await this.versionsUtilService.updateVersion(appVersion, appVersionUpdateDto);
|
||||
|
||||
RequestContext.setLocals(AUDIT_LOGS_REQUEST_CONTEXT_KEY, {
|
||||
|
|
@ -215,6 +215,20 @@ export class VersionService implements IVersionService {
|
|||
});
|
||||
return;
|
||||
}
|
||||
|
||||
async updateAppMode(appId: string, versionId: string, appMode: 'light' | 'dark' | 'auto') {
|
||||
const appVersion = await this.versionRepository.findById(versionId, appId);
|
||||
const updateDto: Partial<AppVersionUpdateDto> = {
|
||||
globalSettings: {
|
||||
theme: {
|
||||
appMode,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return await this.versionsUtilService.updateVersion(appVersion, updateDto as AppVersionUpdateDto);
|
||||
}
|
||||
|
||||
|
||||
promoteVersion(app: App, user: User, promoteVersionDto: PromoteVersionDto) {
|
||||
return dbTransactionWrap(async (manager: EntityManager) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue