Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Order Client Loading States #256

Merged
merged 17 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
4 changes: 3 additions & 1 deletion packages/vendure-order-client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
// TODO set correct version number + date and the changes you've made connected to the PR. See this example for the correct format: https://github.com/Pinelab-studio/pinelab-vendure-plugins/blob/main/packages/vendure-plugin-invoices/CHANGELOG.md
# 2.1.0 (2023-09-20)

- Added loading states for currentUser and activeOrder store ([#256](https://github.com/Pinelab-studio/pinelab-vendure-plugins/pull/256))
22 changes: 16 additions & 6 deletions packages/vendure-order-client/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Vendure Client

A typed, extensible, framework-agnostic client for managing active orders and checkout with Vendure. This package aims to do most of the logic related to active order and checkout management, so that you can focus on presentation with your favorite framework.
A typed, extensible, framework-agnostic client for managing active orders and checkout with Vendure. This package aims to do most of the default logic related to active order and checkout management, so that you can focus on presentation with your favorite framework.

- Sensible, but extendable default GraphQL fields.
- Active order state management.
Expand All @@ -16,18 +16,28 @@ This package should only be used client side, i.e. for fetching an active order,

## Getting started

```ts
This is a Vue.js example, but integrations for React and more are available for @nanostores

```vue
<template>
<div>
<h1 v-if="activeOrder.loading">Loading...</h1>
<h1 v-if="activeOrder.error">Something went wrong!</h1>
<h1 v-if="activeOrder.data">Active order: {{ data.code }}</h1>
</div>
</template>
<script setup lang="ts">
import { VendureOrderClient } from 'vendure-order-client';
import { useStore } from '@nanostores/vue';

const client = new VendureOrderClient(
'http://localhost:3050/shop-api',
'your-channel-token'
);

await client.addItemToOrder('some-id', 1);

// Make this reactive with one of Nanostores' integrations
const total = client.activeOrder.totalWithTax;
const activeOrder = useStore(client.$activeOrder);
await client.getActiveOrder();
</script>
```

## Add your own graphql fields
Expand Down
6 changes: 5 additions & 1 deletion packages/vendure-order-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pinelab/vendure-order-client",
"version": "2.0.0",
"version": "2.1.0",
"description": "A tiny, framework agnostic client for managing active orders and checkout with Vendure.",
"author": "Martijn van de Brug <[email protected]>",
"homepage": "https://pinelab-plugins.com/",
Expand Down Expand Up @@ -32,5 +32,9 @@
"mitt": "^3.0.0",
"nanostores": "^0.9.2"
},
"devDependencies": {
"@nanostores/vue": "^0.10.0",
"@vue/cli": "^5.0.8"
},
"gitHead": "476f36da3aafea41fbf21c70774a30306f1d238f"
}
2 changes: 2 additions & 0 deletions packages/vendure-order-client/src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export class GraphqlQueries {
}
}
}
${this.CURRENT_USER_FIELDS}
`;

LOGIN = gql`
Expand All @@ -355,5 +356,6 @@ export class GraphqlQueries {
}
}
}
${this.CURRENT_USER_FIELDS}
`;
}
68 changes: 68 additions & 0 deletions packages/vendure-order-client/src/store-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { MapStore } from 'nanostores';
import { ErrorCode, ErrorResult } from './graphql-generated-types';

/**
* Interface defining loading and error states per store
*/
export interface StateStore<T> {
loading: boolean;
error: ErrorResult | undefined;
data: T;
}

/**
* Set result as data if not ErrorResult, otherwise set as error in store
*/
export function setResult<T>(
store: MapStore<StateStore<T>>,
result: unknown | ErrorResult
): void {
store.setKey('loading', false);
if ((result as ErrorResult)?.errorCode) {
const errorResult = result as ErrorResult;
store.setKey('error', {
errorCode: errorResult.errorCode,
message: errorResult.message,
});
} else {
store.setKey('data', result as T);
}
}

/**
* Handle loading and error state for given store:
* - Sets loading to true
* - Executes given function
* - Sets loading to false
* - Returns result of function
*
* If an error is caught, it's set as error in the store and thrown.
* Loading state is always set back to false
*/
export function HandleLoadingState(storeName: string) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value; // Save a reference to the original method
descriptor.value = async function (this: any, ...args: any[]) {
const store: MapStore<StateStore<any>> = this[storeName];
store.setKey('loading', true);
if (!store) {
throw new Error(`Store ${storeName} not found`);
}
try {
const result = await originalMethod.apply(this, args);
return result;
} catch (e: any) {
store.setKey('error', {
errorCode: ErrorCode.UnknownError,
message: e?.message,
});
} finally {
store.setKey('loading', false);
}
};
};
}
Loading
Loading