Skip to content

Commit

Permalink
Add set and get methods to PortalValue
Browse files Browse the repository at this point in the history
  • Loading branch information
ibnlanre committed Dec 28, 2023
1 parent 3090799 commit 31274f5
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 90 deletions.
8 changes: 8 additions & 0 deletions src/definition/portal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ export type PortalValue<State> = {
* The BehaviorSubject that contains the current value of the store.
*/
observable: BehaviorSubject<State>;
/**
* The method to set the value of the store.
*/
set?: SetStore<State>;
/**
* The method to get the value of the store.
*/
get?: GetState<State>;
};

/**
Expand Down
201 changes: 118 additions & 83 deletions src/portal/portal.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,90 @@
import { handleSSRError } from "@/utilities";
import { cookieStorage } from "@/cookies";
import { PortalMap, PortalValue, StorageType } from "@/definition";
import {
GetState,
PortalMap,
PortalValue,
SetStore,
StorageType,
} from "@/definition";

import { BehaviorSubject } from "./behaviorSubject";

export class Portal {
private portalMap: PortalMap<any, any> = new Map();

/**
* Deletes the item with the specified path from the internal map.
*
* @template Path The type of the path.
*
* @param {any} path The path of the item to be deleted.
* @returns {void}
*/
private deleteItem = <Path>(path: Path) => {
// Unsubscribe the observable associated with the item
const subject = this.portalMap.get(path);
if (subject) subject.observable.unsubscribe();

try {
// Delete the item from the internal map
this.portalMap.delete(path);
} catch (error) {
console.error(error, `Error occurred while deleting ${path}`);
}
};

/**
* Removes the item with the specified path from the storage.
*
* @template Path The type of the path.
*
* @param {any} path The path of the item to be removed.
* @param {StorageType} storageType The type of storage to remove the item from.
*
* @returns {void}
*/
private deletePersistedItem = <Path extends string>(
path: Path,
storageType: StorageType
): void => {
switch (storageType) {
case "local":
try {
// Remove the path from localStorage
if (typeof localStorage !== "undefined")
localStorage.removeItem(path);
} catch (error) {
console.error(
`Error occurred deleting ${path} from localStorage`,
error
);
}
break;

case "session":
try {
// Remove the path from sessionStorage
if (typeof sessionStorage !== "undefined")
sessionStorage.removeItem(path);
} catch (error) {
console.error(
`Error occurred deleting ${path} from sessionStorage`,
error
);
}
break;

case "cookie":
// Remove the path from document.cookie
cookieStorage.removeItem(path);
break;

default:
console.warn(`Invalid storage type: ${storageType}`);
break;
}
};

/**
* A map of portal entries.
* @type {PortalMap}
Expand Down Expand Up @@ -36,22 +114,52 @@ export class Portal {
*
* @returns {PortalValue<State, Path>} The portal entry with the specified path, or a new portal entry if not found.
*/
getItem = <State, Path extends string>(
insertItem = <State, Path extends string>(
path: Path,
initialState: State
initialState: State,
events: {
set?: SetStore<State>;
get?: GetState<State>;
}
): PortalValue<State> => {
if (this.portalMap.has(path)) {
return this.portalMap.get(path) as PortalValue<State>;
const subject = this.portalMap.get(path) as PortalValue<State>;

if (!subject.get) subject.get = events.get;
if (!subject.set) subject.set = events.set;

return subject;
}

const subject = {
observable: new BehaviorSubject(initialState),
...events,
};

this.portalMap.set(path, subject);

return subject;
};

/**
* Retrieves the value of a portal entry in the portal map.
*
* @template State The type of the state.
* @template Path The type of the path.
*
* @param {Path} path The path of the portal entry.
*
* @returns {State | undefined} The value of the portal entry, or `undefined` if not found.
*/
getItem = <State, Path>(path: Path): State | undefined => {
try {
const subject = this.portalMap.get(path);
if (subject) return subject.observable.value;
} catch (error) {
console.error(error, `Error occured while getting ${path}:`);
}
return undefined;
};

/**
* Sets the value of a portal entry in the portal map.
*
Expand Down Expand Up @@ -80,7 +188,7 @@ export class Portal {
});
}
} catch (error) {
handleSSRError(error, `Error occured while setting ${path}:`);
console.error(error, `Error occured while setting ${path}:`);
}
};

Expand All @@ -96,79 +204,6 @@ export class Portal {
return this.portalMap.has(path);
};

/**
* Deletes the item with the specified path from the internal map.
*
* @template Path The type of the path.
*
* @param {any} path The path of the item to be deleted.
* @returns {void}
*/
private deleteItem = <Path>(path: Path) => {
// Unsubscribe the observable associated with the item
const subject = this.portalMap.get(path);
if (subject) subject.observable.unsubscribe();

try {
// Delete the item from the internal map
this.portalMap.delete(path);
} catch (error) {
handleSSRError(error, `Error occurred while deleting ${path}`);
}
};

/**
* Removes the item with the specified path from the storage.
*
* @template Path The type of the path.
*
* @param {any} path The path of the item to be removed.
* @param {StorageType} storageType The type of storage to remove the item from.
*
* @returns {void}
*/
private deletePersistedItem = <Path extends string>(
path: Path,
storageType: StorageType
): void => {
switch (storageType) {
case "local":
try {
// Remove the path from localStorage
if (typeof localStorage !== "undefined")
localStorage.removeItem(path);
} catch (error) {
console.error(
`Error occurred deleting ${path} from localStorage`,
error
);
}
break;

case "session":
try {
// Remove the path from sessionStorage
if (typeof sessionStorage !== "undefined")
sessionStorage.removeItem(path);
} catch (error) {
console.error(
`Error occurred deleting ${path} from sessionStorage`,
error
);
}
break;

case "cookie":
// Remove the path from document.cookie
cookieStorage.removeItem(path);
break;

default:
console.warn(`Invalid storage type: ${storageType}`);
break;
}
};

/**
* Removes an item from the portal entries and browser storage based on the specified path and storage types.
*
Expand All @@ -194,7 +229,7 @@ export class Portal {
this.deleteItem(path);

// Remove from specified storage types, if provided
if (storageTypes) {
if (storageTypes.length) {
// Check if the DOM is mounted before executing the removal
if (typeof window !== "undefined") removeFromStorageIterator();
else {
Expand All @@ -203,7 +238,7 @@ export class Portal {
}
}
} catch (error) {
handleSSRError(error);
console.error(error, `Error occured while removing ${path}:`);
}
};

Expand All @@ -217,7 +252,7 @@ export class Portal {
this.removeItem(path);
});
} catch (error) {
handleSSRError(error, `Error occured while clearing portal`);
console.error(error, `Error occured while clearing portal`);
}
};
}
2 changes: 1 addition & 1 deletion src/portal/useCookieImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function useCookieImplementation<
Store extends Record<string, any>,
Path extends Paths<Store>,
State extends GetValueByPath<Store, Path>,
Data
Data = State
>(properties: UseCookieImplementation<Store, Path, State, Data>) {
const { path, store, config, initialState } = properties;
const {
Expand Down
2 changes: 1 addition & 1 deletion src/portal/useLocalImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function useLocalImplementation<
Store extends Record<string, any>,
Path extends Paths<Store>,
State extends GetValueByPath<Store, Path>,
Data
Data = State
>(properties: UseLocalImplementation<Store, Path, State, Data>) {
const { path, store, config, initialState } = properties;
const {
Expand Down
11 changes: 7 additions & 4 deletions src/portal/usePortalImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,15 @@ export function usePortalImplementation<
Store extends Record<string, any>,
Path extends Paths<Store>,
State extends GetValueByPath<Store, Path>,
Data
Data = State
>(
properties: UsePortalImplementation<Store, Path, State, Data>
): PortalState<State, Data> {
const { path, store, options, initialState } = properties;
const {
set,
select = (value: State) => value as unknown as Data,
state = initialState as State,
get,
...events
} = { ...options };

/**
Expand All @@ -51,7 +50,11 @@ export function usePortalImplementation<
* Retrieve the portal entry associated with the specified key or create a new one if not found.
* @type {PortalValue<State>}
*/
const { observable } = store.getItem(path, resolvedState);
const { observable, set, get } = store.insertItem(
path,
resolvedState,
events
);

/**
* Subscribe to state changes and update the component's state accordingly.
Expand Down
2 changes: 1 addition & 1 deletion src/portal/useSessionImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function useSessionImplementation<
Store extends Record<string, any>,
Path extends Paths<Store>,
State extends GetValueByPath<Store, Path>,
Data
Data = State
>(properties: UseSessionImplementation<Store, Path, State, Data>) {
const { path, store, config, initialState } = properties;
const {
Expand Down

0 comments on commit 31274f5

Please sign in to comment.