diff --git a/frontend/src/AppBuilder/_hooks/useAppData.js b/frontend/src/AppBuilder/_hooks/useAppData.js
index a7ce7e0e3d..c98127e209 100644
--- a/frontend/src/AppBuilder/_hooks/useAppData.js
+++ b/frontend/src/AppBuilder/_hooks/useAppData.js
@@ -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:
diff --git a/frontend/src/AppBuilder/_stores/slices/appSlice.js b/frontend/src/AppBuilder/_stores/slices/appSlice.js
index b39fe7a581..cfabbeafce 100644
--- a/frontend/src/AppBuilder/_stores/slices/appSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/appSlice.js
@@ -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
diff --git a/frontend/src/AppBuilder/_stores/slices/eventsSlice.js b/frontend/src/AppBuilder/_stores/slices/eventsSlice.js
index 11875e67c3..dfea9c4455 100644
--- a/frontend/src/AppBuilder/_stores/slices/eventsSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/eventsSlice.js
@@ -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
diff --git a/frontend/src/AppBuilder/_stores/utils.js b/frontend/src/AppBuilder/_stores/utils.js
index c96521a099..f70be78661 100644
--- a/frontend/src/AppBuilder/_stores/utils.js
+++ b/frontend/src/AppBuilder/_stores/utils.js
@@ -492,6 +492,7 @@ export function createReferencesLookup(currentState, forQueryParams = false, ini
'logInfo',
'log',
'logError',
+ 'toggleAppMode',
];
const suggestionList = [];
diff --git a/frontend/src/Editor/ActionTypes.js b/frontend/src/Editor/ActionTypes.js
index e3fb69f95d..dac27f6fab 100644
--- a/frontend/src/Editor/ActionTypes.js
+++ b/frontend/src/Editor/ActionTypes.js
@@ -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',
+ },
];
diff --git a/frontend/src/_hooks/useAppDarkMode.js b/frontend/src/_hooks/useAppDarkMode.js
index 7e5b33f87b..2f610031f0 100644
--- a/frontend/src/_hooks/useAppDarkMode.js
+++ b/frontend/src/_hooks/useAppDarkMode.js
@@ -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,
};
diff --git a/frontend/src/_services/appVersion.service.js b/frontend/src/_services/appVersion.service.js
index 9dc59675f3..5423c94ec6 100644
--- a/frontend/src/_services/appVersion.service.js
+++ b/frontend/src/_services/appVersion.service.js
@@ -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,
diff --git a/server/src/modules/apps/service.ts b/server/src/modules/apps/service.ts
index cfbc5280f6..a0f8c18431 100644
--- a/server/src/modules/apps/service.ts
+++ b/server/src/modules/apps/service.ts
@@ -365,6 +365,7 @@ export class AppsService implements IAppsService {
globalSettings: { ...versionToLoad.globalSettings, theme: appTheme },
showViewerNavigation: versionToLoad.showViewerNavigation,
pageSettings: versionToLoad?.pageSettings,
+ appId: app.id,
};
};
diff --git a/server/src/modules/versions/controller.v2.ts b/server/src/modules/versions/controller.v2.ts
index eadac706a1..9600f17298 100644
--- a/server/src/modules/versions/controller.v2.ts
+++ b/server/src/modules/versions/controller.v2.ts
@@ -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')
diff --git a/server/src/modules/versions/interfaces/IControllerV2.ts b/server/src/modules/versions/interfaces/IControllerV2.ts
index f432e9d3dd..4b23435911 100644
--- a/server/src/modules/versions/interfaces/IControllerV2.ts
+++ b/server/src/modules/versions/interfaces/IControllerV2.ts
@@ -7,5 +7,6 @@ export interface IVersionControllerV2 {
getVersion(user: UserEntity, app: AppEntity, mode?: string): Promise
;
updateVersion(user: UserEntity, app: AppEntity, appVersionUpdateDto: AppVersionUpdateDto): Promise;
updateGlobalSettings(user: UserEntity, app: AppEntity, appVersionUpdateDto: AppVersionUpdateDto): Promise;
+ updateAppMode(appId: string, versionId: string, appMode: 'light' | 'dark' | 'auto'): Promise;
promoteVersion(user: UserEntity, app: AppEntity, promoteVersionDto: PromoteVersionDto): Promise;
}
diff --git a/server/src/modules/versions/service.ts b/server/src/modules/versions/service.ts
index 70e7cc3ac8..3f2d1372bf 100644
--- a/server/src/modules/versions/service.ts
+++ b/server/src/modules/versions/service.ts
@@ -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 = {
+ globalSettings: {
+ theme: {
+ appMode,
+ },
+ },
+ };
+
+ return await this.versionsUtilService.updateVersion(appVersion, updateDto as AppVersionUpdateDto);
+ }
+
promoteVersion(app: App, user: User, promoteVersionDto: PromoteVersionDto) {
return dbTransactionWrap(async (manager: EntityManager) => {