aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin J Hoerr <kjhoerr@protonmail.com>2023-05-06 10:50:41 -0400
committerKevin J Hoerr <kjhoerr@protonmail.com>2025-08-18 12:16:07 -0400
commit76702103c1a5f6c54a8aa58875ef21ce3316f4df (patch)
tree916cc98dc83f54c84ecf895c84c263cbaa43d664
parent20c42e6bf24ac332306b92fa27938410ab6a7431 (diff)
downloadpantry-76702103c1a5f6c54a8aa58875ef21ce3316f4df.tar.gz
pantry-76702103c1a5f6c54a8aa58875ef21ce3316f4df.tar.bz2
pantry-76702103c1a5f6c54a8aa58875ef21ce3316f4df.zip
Add labels reducer and hooks
-rw-r--r--src/main/webui/src/hooks/controllers/index.ts1
-rw-r--r--src/main/webui/src/hooks/controllers/labels.ts80
-rw-r--r--src/main/webui/src/hooks/index.ts1
-rw-r--r--src/main/webui/src/hooks/labels.ts21
-rw-r--r--src/main/webui/src/model/index.ts2
-rw-r--r--src/main/webui/src/store/index.ts2
-rw-r--r--src/main/webui/src/store/reducers/labels.ts23
7 files changed, 129 insertions, 1 deletions
diff --git a/src/main/webui/src/hooks/controllers/index.ts b/src/main/webui/src/hooks/controllers/index.ts
index 780fb80..d1903a6 100644
--- a/src/main/webui/src/hooks/controllers/index.ts
+++ b/src/main/webui/src/hooks/controllers/index.ts
@@ -1 +1,2 @@
export * from "./items";
+export * from "./labels";
diff --git a/src/main/webui/src/hooks/controllers/labels.ts b/src/main/webui/src/hooks/controllers/labels.ts
new file mode 100644
index 0000000..e7ac5e0
--- /dev/null
+++ b/src/main/webui/src/hooks/controllers/labels.ts
@@ -0,0 +1,80 @@
+import { request } from "graphql-request";
+
+import {
+ PantryItemLabel,
+ AllLabelsDocument,
+ SyncLabelsDocument,
+ SyncLabelsMutationVariables,
+} from "../../gql/conf/graphql";
+import { ApiError, errorHandler, GraphQLModelError } from "../../model";
+import nullcheck from "../../util/nullcheck";
+import { useToastAPIError } from "../toast";
+import { GRAPHQL_ENDPOINT } from "../../config";
+import { useSetLabels } from "..";
+
+/**
+ * Hook to issue query for `allLabels` to retrieve list of {@link PantryItemLabel}s.
+ *
+ * By default will issue the SET_PANTRY_ITEM_LABELS action with the list of labels.
+ */
+export const useAllLabelsController = (
+ onSuccess?: (items: PantryItemLabel[]) => void,
+ onError?: (error: ApiError) => void,
+) => {
+ const toastApiError = useToastAPIError();
+ const setLabels = useSetLabels();
+
+ /** Issue query for `allLabels` to retrieve list of {@link PantryItemLabel}s. */
+ return () =>
+ request(GRAPHQL_ENDPOINT, AllLabelsDocument)
+ .then((data) => {
+ if (nullcheck(data.allLabels)) {
+ return data.allLabels
+ .filter(nullcheck)
+ .map(({ id, title, color }) => {
+ // ensure object is uncoerced to model type
+ return {
+ id: id ?? undefined,
+ title: title ?? undefined,
+ color: color ?? undefined,
+ };
+ });
+ } else {
+ return Promise.reject(new GraphQLModelError());
+ }
+ })
+ .then(onSuccess ?? setLabels)
+ .catch(errorHandler(onError ?? toastApiError));
+};
+
+/**
+ * Hook to issue a mutation for `syncLabels` to sync labels to the pantry.
+ *
+ * By default will not dispatch any actions.
+ */
+export const useSyncLabelsController = (
+ onSuccess?: (item: PantryItemLabel[]) => void,
+ onError?: (error: ApiError) => void,
+) => {
+ const toastApiError = useToastAPIError();
+ const setLabels = useSetLabels();
+
+ /**
+ * Issue mutation for `syncLabels` to sync labels to the pantry.
+ */
+ return (variables: SyncLabelsMutationVariables) =>
+ request(GRAPHQL_ENDPOINT, SyncLabelsDocument, variables)
+ .then((data) => {
+ if (nullcheck(data) && nullcheck(data?.syncLabels)) {
+ return data.syncLabels.map(label => ({
+ id: label?.id ?? undefined,
+ color: label?.color ?? undefined,
+ title: label?.title ?? undefined,
+ }));
+ } else {
+ return Promise.reject(new GraphQLModelError());
+ }
+ })
+ .then(onSuccess ?? setLabels)
+ .catch(errorHandler(onError ?? toastApiError));
+};
diff --git a/src/main/webui/src/hooks/index.ts b/src/main/webui/src/hooks/index.ts
index 15d42ef..4580ec8 100644
--- a/src/main/webui/src/hooks/index.ts
+++ b/src/main/webui/src/hooks/index.ts
@@ -1,5 +1,6 @@
export * from "./health";
export * from "./items";
+export * from "./labels";
export * from "./toast";
export * from "./controllers";
export { useDispatch, useSelector } from "../store";
diff --git a/src/main/webui/src/hooks/labels.ts b/src/main/webui/src/hooks/labels.ts
new file mode 100644
index 0000000..5d4fee3
--- /dev/null
+++ b/src/main/webui/src/hooks/labels.ts
@@ -0,0 +1,21 @@
+import { PantryItemLabel } from "../model";
+import { useDispatch } from "../store";
+import { addLabel, setLabels } from "../store/reducers/labels";
+
+/**
+ * Hook to dispatch a {@link SetLabelsAction}.
+ */
+export const useSetLabels = () => {
+ const dispatch = useDispatch();
+
+ return (Labels: PantryItemLabel[]) => dispatch(setLabels(Labels));
+};
+
+/**
+ * Hook to dispatch an {@link AddLabelAction}.
+ */
+export const useAddLabel = () => {
+ const dispatch = useDispatch();
+
+ return (Label: PantryItemLabel) => dispatch(addLabel(Label));
+};
diff --git a/src/main/webui/src/model/index.ts b/src/main/webui/src/model/index.ts
index 61f1cf0..c75dceb 100644
--- a/src/main/webui/src/model/index.ts
+++ b/src/main/webui/src/model/index.ts
@@ -1,4 +1,4 @@
export * from "./errors";
export * from "./health";
export * from "./toastMessage";
-export type { PantryItem } from "../gql/conf/graphql";
+export type { PantryItem, PantryItemLabel } from "../gql/conf/graphql";
diff --git a/src/main/webui/src/store/index.ts b/src/main/webui/src/store/index.ts
index b0071c4..f4eb1f3 100644
--- a/src/main/webui/src/store/index.ts
+++ b/src/main/webui/src/store/index.ts
@@ -7,6 +7,7 @@ import {
import healthReducer from "./reducers/health";
import itemsReducer from "./reducers/items";
+import labelsReducer from "./reducers/labels";
import toastReducer from "./reducers/toast";
// creating store
@@ -14,6 +15,7 @@ export const store = configureStore({
reducer: {
health: healthReducer,
items: itemsReducer,
+ labels: labelsReducer,
toast: toastReducer,
},
});
diff --git a/src/main/webui/src/store/reducers/labels.ts b/src/main/webui/src/store/reducers/labels.ts
new file mode 100644
index 0000000..8974af5
--- /dev/null
+++ b/src/main/webui/src/store/reducers/labels.ts
@@ -0,0 +1,23 @@
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+
+import { PantryItemLabel } from "../../model";
+
+export type LabelState = Array<PantryItemLabel>;
+
+const initialState: LabelState = [];
+
+const labelsSlice = createSlice({
+ name: "labels",
+ initialState,
+ reducers: {
+ setLabels: (_state, action: PayloadAction<Array<PantryItemLabel>>) =>
+ action.payload,
+ addLabel: (state, action: PayloadAction<PantryItemLabel>) => {
+ state.push(action.payload);
+ },
+ },
+});
+
+export const { addLabel, setLabels } = labelsSlice.actions;
+
+export default labelsSlice.reducer;