@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
156 lines (155 loc) • 6.64 kB
JavaScript
// SPDX-License-Identifier: Apache-2.0
import GirafeSingleton from '../../base/GirafeSingleton.js';
import { createObjectFromPath, deletePropertyByPath, getPropertyByPath, setPropertyByPath, mergeObjects } from '../utils/pathUtils.js';
/**
* Manages user data storage and retrieval across different storage sources.
* The class supports localStorage and server as storage options (server-side implementation currently not available).
* It provides functionality to set, get, save, and delete user data while maintaining the flexibility to target specific paths
* within the user data object.
*/
class UserDataManager extends GirafeSingleton {
// Using the full URL as storage key allows having separate user data for apps under the same base URL (e.g. demo apps)
storageKey = window.location.origin + window.location.pathname;
config;
allowSaveUserPreferences = true;
initializeFromConfig(config) {
this.config = config;
}
userData;
userDataPromise;
/**
* Retrieves a specific part of the user data defined by the storage path.
* @param storagePath path in the user data object to the requested value.
* @returns the partial user data object.
*/
async getUserData(storagePath) {
if (this.userData) {
// User Data were already loaded
return this.extractValue(this.userData, storagePath);
}
this.userDataPromise = this.load().catch((error) => {
this.userDataPromise = undefined;
throw error;
});
this.userData = await this.userDataPromise;
return this.extractValue(this.userData, storagePath);
}
getFromLocalStorage(storagePath) {
const storedUserData = localStorage.getItem(this.storageKey);
const userData = storedUserData ? JSON.parse(storedUserData) : {};
return this.extractValue(userData, storagePath);
}
extractValue(data, storagePath) {
const { found, parentObject, lastKey } = getPropertyByPath(data, storagePath);
return found && parentObject && lastKey ? parentObject[lastKey] : undefined;
}
async load() {
if (this.config.source === 'localStorage') {
return this.loadFromLocalStorage();
}
else if (this.config.source === 'server') {
const userdata = await this.loadFromServer();
return userdata;
}
throw new Error(`Unknown source for user preferences: ${this.config.source}`);
}
loadFromLocalStorage() {
const storedUserData = localStorage.getItem(this.storageKey);
return storedUserData ? JSON.parse(storedUserData) : {};
}
async loadFromServer() {
try {
const response = await fetch(this.config.getUrl);
const storedUserData = await response.json();
return storedUserData;
}
catch {
// Cannot load user preference from server.
// In this case, we deactivate the save process for user preferences
// (because we do not want to override the existing user-preferences if they could not be loaded for some reason)
// And we return empty preferences, because we want to be able to start the application
// even if the user preferences could not be retrieved.
this.allowSaveUserPreferences = false;
this.context.errorManager.pushMessage('loadUserPreferencesError', 'User Preferences could not be loaded. Try refreshing the page or contact your administrator.', 'error');
return {};
}
}
async saveUserData(storagePath, newValue) {
if (!getPropertyByPath(this.userData, storagePath).found) {
// Object does not exist in user data yet, create it
const newUserData = createObjectFromPath(storagePath);
this.userData = mergeObjects(this.userData ?? {}, newUserData);
}
if (setPropertyByPath(this.userData, storagePath, newValue)) {
await this.save(this.userData ?? {});
}
}
saveToLocalStorage(storagePath, newValue) {
let fullUserDataObject = this.loadFromLocalStorage();
if (!getPropertyByPath(fullUserDataObject, storagePath).found) {
// Object does not exist in user data yet, create it
const newUserData = createObjectFromPath(storagePath);
fullUserDataObject = mergeObjects(fullUserDataObject, newUserData);
}
if (setPropertyByPath(fullUserDataObject, storagePath, newValue)) {
this.writeToLocalStorage(fullUserDataObject);
}
}
async save(fullUserDataObject) {
if (this.config.source === 'localStorage') {
this.writeToLocalStorage(fullUserDataObject);
}
else if (this.config.source === 'server') {
await this.writeToServer(fullUserDataObject);
}
else {
throw new Error(`Unknown source for user preferences: ${this.config.source}`);
}
}
writeToLocalStorage(fullUserDataObject) {
localStorage.setItem(this.storageKey, JSON.stringify(fullUserDataObject));
}
async writeToServer(fullUserDataObject) {
if (!this.allowSaveUserPreferences) {
return;
}
const response = await fetch(this.config.postUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(fullUserDataObject)
});
if (!response.ok) {
console.error(response.text);
throw new Error(`Error while saving user preferences: ${response.status}`);
}
}
async deleteUserData(storagePath) {
deletePropertyByPath(this.userData, storagePath);
await this.save(this.userData);
}
async deleteAllUserData() {
this.userData = undefined;
if (this.config.source === 'localStorage') {
localStorage.removeItem(this.storageKey);
}
else if (this.config.source === 'server') {
await this.deleteFromServer();
}
else {
throw new Error(`Unknown source for user preferences: ${this.config.source}`);
}
}
async deleteFromServer() {
if (!this.allowSaveUserPreferences) {
return;
}
const response = await fetch(this.config.postUrl, {
method: 'DELETE'
});
if (!response.ok) {
console.error(response.text);
throw new Error(`Error while deleting user preferences: ${response.status}`);
}
}
}
export default UserDataManager;