User settings
Per-user preferences stored on the server, with a shape you extend yourself — and the difference between save and patch.
User settings are a free-form object the server keeps per user. The library knows about three keys and lets you add whatever else your application needs.
await client.api.userSettings.get(); // load
await client.api.userSettings.patch(changes); // merge, and get the new state back
await client.api.userSettings.save(settings); // replace wholesale
await client.api.userSettings.reset(); // empty thempatch unless you mean to replace
This is the distinction that matters:
| Sends | Returns | Use it for | |
|---|---|---|---|
patch(partial) | Only your keys | The merged settings | Almost always |
save(settings) | The whole object | void | A settings screen that owns the entire object |
save replaces. A view that saves its own slice — a sidebar remembering its width — wipes every other
setting on the way. And it returns nothing, so you cannot tell what the state became.
Typing your own settings
UserSettings is { language?, skipCount?, email? } & Record<string, any>, so extra keys are accepted. Declare
your own shape and pass it as the generic:
import { createAtomicClient } from "@sinequa/atomic";
import type { UserSettings } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
/** Your application's settings, on top of the ones the library declares. */
type MySettings = UserSettings & {
resultsPerPage?: number;
savedSearches?: { name: string; text: string }[];
sidebarWidth?: number;
};
// Load once at start-up; they change rarely and every read is a round trip.
let settings = await client.api.userSettings.get<MySettings>();
/** Merge one change. Never `save` from a view that owns only part of the object. */
export async function update(change: Partial<MySettings>) {
settings = await client.api.userSettings.patch<MySettings>(change);
return settings;
}
export async function addSavedSearch(name: string, text: string) {
// Read-modify-write on an array: patch merges keys, it does not append to arrays.
const saved = [...(settings.savedSearches ?? []), { name, text }];
return update({ savedSearches: saved });
}
export function pageSize() {
return settings.resultsPerPage ?? 10;
}import { Injectable, computed, inject, resource, signal } from "@angular/core";
import type { UserSettings } from "@sinequa/atomic";
import { SINEQUA } from "./sinequa";
/** Your application's settings, on top of the ones the library declares. */
export type MySettings = UserSettings & {
resultsPerPage?: number;
savedSearches?: { name: string; text: string }[];
sidebarWidth?: number;
};
@Injectable({ providedIn: "root" })
export class SettingsService {
private readonly client = inject(SINEQUA);
private readonly local = signal<MySettings | undefined>(undefined);
/** Loaded once; the local signal holds the authoritative value after a patch. */
private readonly loaded = resource({
loader: async () => {
const settings = await this.client.api.userSettings.get<MySettings>();
this.local.set(settings);
return settings;
},
});
readonly settings = computed(() => this.local() ?? this.loaded.value());
readonly pageSize = computed(() => this.settings()?.resultsPerPage ?? 10);
/** Merge one change. `save` would replace the whole object — see the pitfalls. */
async update(change: Partial<MySettings>) {
const merged = await this.client.api.userSettings.patch<MySettings>(change);
this.local.set(merged);
return merged;
}
async addSavedSearch(name: string, text: string) {
// patch merges keys; it does not append to an array.
const saved = [...(this.settings()?.savedSearches ?? []), { name, text }];
return this.update({ savedSearches: saved });
}
}import type { UserSettings } from "@sinequa/atomic";
import { useCallback, useEffect, useState } from "react";
import { ready, sinequa } from "./sinequa";
/** Your application's settings, on top of the ones the library declares. */
export type MySettings = UserSettings & {
resultsPerPage?: number;
savedSearches?: { name: string; text: string }[];
sidebarWidth?: number;
};
export function useSettings() {
const [settings, setSettings] = useState<MySettings>();
// Loaded once: they change rarely, and every read is a round trip.
useEffect(() => {
let active = true;
ready
.then(() => sinequa.api.userSettings.get<MySettings>())
.then((next) => active && setSettings(next));
return () => {
active = false;
};
}, []);
/** Merge one change. `save` would replace the whole object — see the pitfalls. */
const update = useCallback(async (change: Partial<MySettings>) => {
const merged = await sinequa.api.userSettings.patch<MySettings>(change);
setSettings(merged);
return merged;
}, []);
const addSavedSearch = useCallback(
(name: string, text: string) =>
// patch merges keys; it does not append to an array.
update({ savedSearches: [...(settings?.savedSearches ?? []), { name, text }] }),
[settings, update],
);
return { settings, update, addSavedSearch, pageSize: settings?.resultsPerPage ?? 10 };
}UserSettingsKeys is a small convenience: it autocompletes the known keys without rejecting your own, using the
StringWithAutocomplete trick.