Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9ac5229
wip
rubenthoms Nov 27, 2025
a4aeeab
wip
rubenthoms Nov 28, 2025
f836140
Merge remote-tracking branch 'equinor/main' into dpf-make-settings-an…
rubenthoms Nov 28, 2025
aed0a17
wip
rubenthoms Nov 29, 2025
2c85714
wip
rubenthoms Dec 1, 2025
8bb066d
wip
rubenthoms Dec 2, 2025
9710570
Implemented handling of invalid persisted data structures for settings
rubenthoms Dec 8, 2025
ed0419e
Removed setting categories
rubenthoms Dec 9, 2025
04d47da
Multiple bug fixes
rubenthoms Dec 10, 2025
ffbca32
Fixed missing renaming
rubenthoms Dec 11, 2025
5a9b435
changed brand name
rubenthoms Dec 12, 2025
c957797
Merge remote-tracking branch 'equinor/main' into dpf-make-settings-an…
rubenthoms Dec 12, 2025
ddd1a8f
fix: attribute updates should trigger setting update due to deps loading
rubenthoms Dec 12, 2025
d305131
Merge remote-tracking branch 'equinor/main' into dpf-make-settings-an…
rubenthoms Dec 29, 2025
adb5c67
New logic allowing groups to provide valueRanges for settings
rubenthoms Dec 29, 2025
0d077d8
fix: linting
rubenthoms Jan 2, 2026
bddc1a6
Several fixes
rubenthoms Jan 6, 2026
a6c3299
Merge branch 'main' into dpf-make-settings-and-available-settings-ind…
rubenthoms Jan 6, 2026
4c6e42a
Renamed "valueRange" to "valueConstraints"
rubenthoms Jan 6, 2026
74c004d
Merge remote-tracking branch 'origin/dpf-make-settings-and-available-…
rubenthoms Jan 6, 2026
000f4ba
Missing file
rubenthoms Jan 6, 2026
f28a165
Adjustments after review and change of deserialization pattern
rubenthoms Jan 8, 2026
d6ffc37
Merge branch 'main' into dpf-make-settings-and-available-settings-ind…
rubenthoms Jan 8, 2026
453da01
Merge remote-tracking branch 'origin/dpf-make-settings-and-available-…
rubenthoms Jan 8, 2026
35f3539
Fixes after merge
rubenthoms Jan 8, 2026
871bf17
Merge remote-tracking branch 'equinor/main' into dpf-groups-with-own-…
rubenthoms Jan 8, 2026
b83e8c0
Added value change handling for shared settings
rubenthoms Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PublishSubscribeDelegate, type PublishSubscribe } from "@lib/utils/PublishSubscribeDelegate";
import { UnsubscribeFunctionsManagerDelegate } from "@lib/utils/UnsubscribeFunctionsManagerDelegate";

import { DataProviderManagerTopic, type GlobalSettings } from "../framework/DataProviderManager/DataProviderManager";
Expand All @@ -11,21 +12,34 @@ import type {
import type { Item } from "../interfacesAndTypes/entities";
import type { SerializedSettingsState } from "../interfacesAndTypes/serialization";
import type { MakeSettingTypesMap, SettingsKeysFromTuple } from "../interfacesAndTypes/utils";
import type { Settings } from "../settings/settingsDefinitions";
import { SettingRegistry } from "../settings/SettingRegistry";
import type { Settings, SettingTypeDefinitions } from "../settings/settingsDefinitions";

import { Dependency } from "./_utils/Dependency";

export enum SharedSettingsDelegateTopic {
SETTINGS_CHANGED = "SHARED_SETTINGS_DELEGATE_SETTINGS_CHANGED",
}

export type SharedSettingsDelegatePayloads = {
[SharedSettingsDelegateTopic.SETTINGS_CHANGED]: void;
};

export class SharedSettingsDelegate<
TSettings extends Settings,
TSettingTypes extends MakeSettingTypesMap<TSettings> = MakeSettingTypesMap<TSettings>,
TSettingKey extends SettingsKeysFromTuple<TSettings> = SettingsKeysFromTuple<TSettings>,
> {
> implements PublishSubscribe<SharedSettingsDelegatePayloads>
{
private _publishSubscribeDelegate: PublishSubscribeDelegate<SharedSettingsDelegatePayloads> =
new PublishSubscribeDelegate<SharedSettingsDelegatePayloads>();
private _externalSettingControllers: { [K in TSettingKey]: ExternalSettingController<K> } = {} as {
[K in TSettingKey]: ExternalSettingController<K>;
};
private _wrappedSettings: { [K in TSettingKey]: SettingManager<K> } = {} as {
[K in TSettingKey]: SettingManager<K>;
};
private _internalSettings: Map<TSettingKey, SettingManager<any>> = new Map();
private _unsubscribeFunctionsManagerDelegate: UnsubscribeFunctionsManagerDelegate =
new UnsubscribeFunctionsManagerDelegate();
private _dependencies: Dependency<any, TSettings, any, any>[] = [];
Expand All @@ -45,18 +59,52 @@ export class SharedSettingsDelegate<
this._parentItem = parentItem;
this._customDependenciesDefinition = customDependenciesDefinition ?? null;

for (const key in wrappedSettings) {
const setting = wrappedSettings[key];
const externalSettingController = new ExternalSettingController(parentItem, setting);
this._externalSettingControllers[key] = externalSettingController;
}

const dataProviderManager = parentItem.getItemDelegate().getDataProviderManager();
if (!dataProviderManager) {
throw new Error("SharedSettingDelegate must have a parent item with a data provider manager.");
}

// Create dependencies first, which may populate _internalSettings
this.createDependencies();

// Now create external controllers, passing internal settings if they exist
for (const key in wrappedSettings) {
const setting = wrappedSettings[key];
const internalSetting = this._internalSettings.get(key);
const externalSettingController = new ExternalSettingController(parentItem, setting, internalSetting);
this._externalSettingControllers[key] = externalSettingController;

// Subscribe to changes in the external setting controller to notify listeners
this._unsubscribeFunctionsManagerDelegate.registerUnsubscribeFunction(
"externalSettingControllers",
externalSettingController
.getSetting()
.getPublishSubscribeDelegate()
.makeSubscriberFunction(SettingTopic.VALUE)(() => {
this.handleSettingChanged();
}),
);
}
}

getPublishSubscribeDelegate(): PublishSubscribeDelegate<SharedSettingsDelegatePayloads> {
return this._publishSubscribeDelegate;
}

makeSnapshotGetter<T extends SharedSettingsDelegateTopic.SETTINGS_CHANGED>(
topic: T,
): () => SharedSettingsDelegatePayloads[T] {
const snapshotGetter = (): any => {
if (topic === SharedSettingsDelegateTopic.SETTINGS_CHANGED) {
return;
}
};

return snapshotGetter;
}

private handleSettingChanged(): void {
this._publishSubscribeDelegate.notifySubscribers(SharedSettingsDelegateTopic.SETTINGS_CHANGED);
}

getWrappedSettings(): { [K in TSettingKey]: SettingManager<K> } {
Expand All @@ -77,6 +125,10 @@ export class SharedSettingsDelegate<
const setting = this._wrappedSettings[key];
setting.beforeDestroy();
}
for (const internalSetting of this._internalSettings.values()) {
internalSetting.beforeDestroy();
}
this._internalSettings.clear();
for (const dependency of this._dependencies) {
dependency.beforeDestroy();
}
Expand Down Expand Up @@ -181,6 +233,61 @@ export class SharedSettingsDelegate<
return this._parentItem.getItemDelegate().getDataProviderManager().getGlobalSetting(key);
};

const valueConstraintsUpdater = <K extends TSettingKey>(
settingKey: K,
updateFunc: UpdateFunc<
SettingTypeDefinitions[K]["valueConstraints"],
TSettings,
TSettingTypes,
TSettingKey
>,
): Dependency<SettingTypeDefinitions[K]["valueConstraints"], TSettings, TSettingTypes, TSettingKey> => {
// Create an internal setting for this key if it doesn't exist yet
// This setting will hold the group's own value range
if (!this._internalSettings.has(settingKey)) {
const internalSetting = SettingRegistry.makeSetting(settingKey, null);
// Mark as loading initially so the intersection waits for the value range to be computed
internalSetting.setLoading(true);
this._internalSettings.set(settingKey, internalSetting);
}

const internalSetting = this._internalSettings.get(settingKey)!;

const dependency = new Dependency<
SettingTypeDefinitions[K]["valueConstraints"],
TSettings,
TSettingTypes,
TSettingKey
>(
localSettingManagerGetter.bind(this),
globalSettingGetter.bind(this),
updateFunc,
makeLocalSettingGetter,
loadingStateGetter,
makeGlobalSettingGetter,
);
this._dependencies.push(dependency);

dependency.subscribe((valueRange) => {
// Set the value range on the internal setting, not the wrapped setting
if (valueRange === null) {
internalSetting.setValueConstraints(null as SettingTypeDefinitions[K]["valueConstraints"]);
return;
}
internalSetting.setValueConstraints(valueRange);
});

dependency.subscribeLoading((loading: boolean) => {
if (loading) {
internalSetting.setLoading(loading);
}
});

dependency.initialize();

return dependency;
};

const settingAttributesUpdater = <K extends TSettingKey>(
settingKey: K,
updateFunc: UpdateFunc<Partial<SettingAttributes>, TSettings, TSettingTypes, TSettingKey>,
Expand All @@ -207,9 +314,41 @@ export class SharedSettingsDelegate<
return dependency;
};

const helperDependency = <T>(
update: (args: {
getLocalSetting: <T extends TSettingKey>(settingName: T) => TSettingTypes[T];
getGlobalSetting: <T extends keyof GlobalSettings>(settingName: T) => GlobalSettings[T];
getHelperDependency: <TDep>(
dep: Dependency<TDep, TSettings, TSettingTypes, TSettingKey>,
) => Awaited<TDep> | null;
abortSignal: AbortSignal;
}) => T,
) => {
const dependency = new Dependency<T, TSettings, TSettingTypes, TSettingKey>(
localSettingManagerGetter.bind(this),
globalSettingGetter.bind(this),
update,
makeLocalSettingGetter,
loadingStateGetter,
makeGlobalSettingGetter,
);
this._dependencies.push(dependency);

dependency.initialize();

return dependency;
};

const dataProviderManager = this._parentItem.getItemDelegate().getDataProviderManager();

if (this._customDependenciesDefinition) {
this._customDependenciesDefinition({
settingAttributesUpdater: settingAttributesUpdater.bind(this),
valueConstraintsUpdater: valueConstraintsUpdater.bind(this),
helperDependency: helperDependency.bind(this),
workbenchSession: dataProviderManager.getWorkbenchSession(),
workbenchSettings: dataProviderManager.getWorkbenchSettings(),
queryClient: dataProviderManager.getQueryClient(),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,33 @@ export class ExternalSettingController<
TExternalValue extends SettingTypeDefinitions[TSetting]["externalValue"] | null =
| SettingTypeDefinitions[TSetting]["externalValue"]
| null,
TValueConstraints extends SettingTypeDefinitions[TSetting]["valueConstraints"] = SettingTypeDefinitions[TSetting]["valueConstraints"],
TValueConstraints extends
SettingTypeDefinitions[TSetting]["valueConstraints"] = SettingTypeDefinitions[TSetting]["valueConstraints"],
> {
private _parentItem: Item;
private _setting: SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>;
private _controlledSettings: Map<string, SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>> =
new Map();
private _controlledSettings: Map<
string,
SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>
> = new Map();
private _valueConstraintsMap: Map<string, TValueConstraints | null> = new Map();
private _unsubscribeFunctionsManagerDelegate: UnsubscribeFunctionsManagerDelegate =
new UnsubscribeFunctionsManagerDelegate();

constructor(parentItem: Item, setting: SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>) {
private _additionalControlledSetting: SettingManager<
TSetting,
TInternalValue,
TExternalValue,
TValueConstraints
> | null = null;

constructor(
parentItem: Item,
setting: SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>,
additionalControlledSetting?: SettingManager<TSetting, TInternalValue, TExternalValue, TValueConstraints>,
) {
this._parentItem = parentItem;
this._setting = setting;
this._additionalControlledSetting = additionalControlledSetting ?? null;

const dataProviderManager = parentItem.getItemDelegate().getDataProviderManager();
this._unsubscribeFunctionsManagerDelegate.registerUnsubscribeFunction(
Expand Down Expand Up @@ -128,6 +142,12 @@ export class ExternalSettingController<
}

const settings = this.findControlledSettingsRecursively(parentGroup, this._parentItem);

// Add the additional controlled setting to the array if provided
if (this._additionalControlledSetting) {
settings.push(this._additionalControlledSetting);
}

for (const setting of settings) {
if (setting.isExternallyControlled()) {
continue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { isDevMode } from "@lib/utils/devMode";
import { UnsubscribeFunctionsManagerDelegate } from "@lib/utils/UnsubscribeFunctionsManagerDelegate";

import { GroupDelegate } from "../../delegates/GroupDelegate";
import { ItemDelegate } from "../../delegates/ItemDelegate";
import { SharedSettingsDelegate } from "../../delegates/SharedSettingsDelegate";
import { SharedSettingsDelegate, SharedSettingsDelegateTopic } from "../../delegates/SharedSettingsDelegate";
import type { GroupType } from "../../groups/groupTypes";
import type {
CustomGroupImplementation,
Expand All @@ -15,7 +16,7 @@ import type { SerializedGroup, SerializedSettingsState } from "../../interfacesA
import { SerializedType } from "../../interfacesAndTypes/serialization";
import type { MakeSettingTypesMap, SettingsKeysFromTuple } from "../../interfacesAndTypes/utils";
import type { Settings } from "../../settings/settingsDefinitions";
import type { DataProviderManager } from "../DataProviderManager/DataProviderManager";
import { DataProviderManagerTopic, type DataProviderManager } from "../DataProviderManager/DataProviderManager";
import type { SettingManager } from "../SettingManager/SettingManager";
import { makeSettings } from "../utils/makeSettings";

Expand Down Expand Up @@ -59,6 +60,8 @@ export class Group<
private _icon: React.ReactNode | null = null;
private _emptyContentMessage: string | null = null;
private _sharedSettingsDelegate: SharedSettingsDelegate<TSettings, TSettingTypes, TSettingKey> | null = null;
private _unsubscribeFunctionsManagerDelegate: UnsubscribeFunctionsManagerDelegate =
new UnsubscribeFunctionsManagerDelegate();

constructor(params: GroupParams<TSettings, TSettingTypes>) {
const { dataProviderManager, customGroupImplementation, type } = params;
Expand All @@ -76,13 +79,25 @@ export class Group<
| ((args: DefineBasicDependenciesArgs<TSettings, TSettingTypes>) => void)
| undefined,
);
this._unsubscribeFunctionsManagerDelegate.registerUnsubscribeFunction(
"shared-settings",
this._sharedSettingsDelegate
.getPublishSubscribeDelegate()
.makeSubscriberFunction(SharedSettingsDelegateTopic.SETTINGS_CHANGED)(() =>
this.handleSettingsChange(),
),
);
}
this._type = type;
this._emptyContentMessage = customGroupImplementation.getEmptyContentMessage
? customGroupImplementation.getEmptyContentMessage()
: null;
}

handleSettingsChange() {
this._itemDelegate.getDataProviderManager().publishTopic(DataProviderManagerTopic.DATA_REVISION);
}

getItemDelegate(): ItemDelegate {
return this._itemDelegate;
}
Expand Down Expand Up @@ -137,5 +152,6 @@ export class Group<
beforeDestroy(): void {
this._groupDelegate.beforeDestroy();
this._sharedSettingsDelegate?.beforeDestroy();
this._unsubscribeFunctionsManagerDelegate.unsubscribeAll();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { getDrilledWellboreHeadersOptions } from "@api";
import { IntersectionType } from "@framework/types/intersection";

import { Setting } from "../..//settings/settingsDefinitions";
import { getAvailableIntersectionOptions } from "../../dataProviders/dependencyFunctions/sharedSettingUpdaterFunctions";
import type { CustomGroupImplementationWithSettings } from "../../interfacesAndTypes/customGroupImplementation";
import type { DefineBasicDependenciesArgs } from "../../interfacesAndTypes/customSettingsHandler";
import type { MakeSettingTypesMap } from "../../interfacesAndTypes/utils";
Expand All @@ -18,13 +20,43 @@ export class IntersectionView implements CustomGroupImplementationWithSettings<I

defineDependencies({
settingAttributesUpdater,
helperDependency,
valueConstraintsUpdater,
queryClient,
}: DefineBasicDependenciesArgs<IntersectionViewSettings, SettingTypes>): void {
settingAttributesUpdater(Setting.WELLBORE_EXTENSION_LENGTH, ({ getLocalSetting }) => {
const intersection = getLocalSetting(Setting.INTERSECTION);
const enableExtensionLength = intersection?.type === IntersectionType.WELLBORE;

return { enabled: enableExtensionLength };
});

const wellboreHeadersDep = helperDependency(async ({ getGlobalSetting, abortSignal }) => {
const fieldIdentifier = getGlobalSetting("fieldId");

if (!fieldIdentifier) {
return null;
}

return await queryClient.fetchQuery({
...getDrilledWellboreHeadersOptions({
query: { field_identifier: fieldIdentifier },
signal: abortSignal,
}),
});
});

valueConstraintsUpdater(Setting.INTERSECTION, ({ getHelperDependency, getGlobalSetting }) => {
const wellboreHeaders = getHelperDependency(wellboreHeadersDep) ?? [];
const intersectionPolylines = getGlobalSetting("intersectionPolylines");
const fieldIdentifier = getGlobalSetting("fieldId");

const fieldIntersectionPolylines = intersectionPolylines.filter(
(intersectionPolyline) => intersectionPolyline.fieldId === fieldIdentifier,
);

return getAvailableIntersectionOptions(wellboreHeaders, fieldIntersectionPolylines);
});
}

getDefaultSettingsValues() {
Expand Down
Loading
Loading