Skip to content

Commit e925263

Browse files
committed
Add Shopify pagination
1 parent 4723fe3 commit e925263

File tree

13 files changed

+349
-66
lines changed

13 files changed

+349
-66
lines changed

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/Render/Products.cshtml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
{
88
<dt class="product-title">@product.Title</dt>
99
<dd class="product-body">@Html.Raw(product.Body)</dd>
10-
<img class="product-image" src="@product.Image" alt="@product.Title" />
10+
@if (!string.IsNullOrEmpty(product.Image))
11+
{
12+
<img class="product-image" src="@product.Image" alt="@product.Title" />
13+
}
1114
}
1215
</dl>
1316
}

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/Render/ProductsV9.cshtml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
{
88
<dt class="product-title">@product.Title</dt>
99
<dd class="product-body">@Html.Raw(product.Body)</dd>
10-
<img class="product-image" src="@product.Image" alt="@product.Title" />
10+
@if (!string.IsNullOrEmpty(product.Image))
11+
{
12+
<img class="product-image" src="@product.Image" alt="@product.Title" />
13+
}
1114
}
1215
</dl>
13-
}
14-
15-
16-
16+
}

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/productPicker.controller.js

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,43 @@
1414
}
1515
}
1616

17+
// pagination
18+
vm.pagination = {
19+
pageNumber: 1,
20+
totalPages: 1,
21+
previousPageInfo: '',
22+
nextPageInfo: ''
23+
};
24+
vm.nextPage = nextPage;
25+
vm.prevPage = prevPage;
26+
vm.changePage = togglePage;
27+
vm.goToPage = togglePage;
28+
1729
// step 1. check configuration
1830
checkConfiguration(function () {
19-
// step 2. get products
20-
getProducts();
31+
// step 2.1 get total pages
32+
getTotalPages();
33+
// step 2.2 get products
34+
getProducts('');
2135
});
2236

23-
function getProducts() {
37+
function getTotalPages() {
38+
umbracoCmsIntegrationsCommerceShopifyResource.getTotalPages().then(function (response) {
39+
vm.pagination.totalPages = response;
40+
});
41+
}
42+
43+
function getProducts(pageInfo) {
2444
vm.loading = true;
2545

26-
umbracoCmsIntegrationsCommerceShopifyResource.getProductsList().then(function (response) {
46+
umbracoCmsIntegrationsCommerceShopifyResource.getProductsList(pageInfo).then(function (response) {
2747
if (response.isValid) {
28-
48+
if (response.previousPageInfo) {
49+
vm.pagination.previousPageInfo = response.previousPageInfo;
50+
}
51+
if (response.nextPageInfo) {
52+
vm.pagination.nextPageInfo = response.nextPageInfo;
53+
}
2954
vm.productsList = response.result.products;
3055

3156
if ($scope.model.value != undefined && $scope.model.value.length > 0) {
@@ -86,8 +111,9 @@
86111
}
87112

88113
vm.remove = function (node) {
89-
vm.submit(vm.selectedProducts.filter(el => el.id != node.alias));
90-
loadProductsPreview();
114+
vm.previewNodes = vm.previewNodes.filter(el => el.alias != node.alias);
115+
vm.selectedProducts = vm.selectedProducts.filter(el => el.id != node.alias);
116+
vm.submit(vm.previewNodes.length == 0 ? [] : vm.selectedProducts.filter(el => el.id != node.alias));
91117
}
92118

93119
function checkConfiguration(callback) {
@@ -114,19 +140,17 @@
114140
vm.previewNodes = [];
115141

116142
var ids = $scope.model.value.split(",");
117-
118-
var list = vm.productsList.filter(el => {
119-
var id = ids.find(e => e == el.id);
120-
return id !== undefined ? el : null;
121-
});
122-
vm.selectedProducts = list;
123-
124-
list.forEach(el => {
125-
vm.previewNodes.push({
126-
icon: "icon-shopping-basket",
127-
name: el.title,
128-
alias: el.id
129-
});
143+
umbracoCmsIntegrationsCommerceShopifyResource.getProductsByIds(ids).then(function (response) {
144+
if (response.isValid) {
145+
response.result.products.forEach(el => {
146+
vm.selectedProducts.push(el);
147+
vm.previewNodes.push({
148+
icon: "icon-shopping-basket",
149+
name: el.title,
150+
alias: el.id
151+
});
152+
})
153+
}
130154
});
131155
}
132156

@@ -146,6 +170,28 @@
146170
if (vm.config.validationLimit.max != null)
147171
return vm.config.validationLimit.max >= updatedCount;
148172
}
173+
174+
// pagination events
175+
function nextPage(pageNumber) {
176+
getProducts(vm.pagination.nextPageInfo);
177+
}
178+
179+
function prevPage(pageNumber) {
180+
getProducts(vm.pagination.previousPageInfo);
181+
}
182+
183+
function togglePage(pageNumber) {
184+
if (pageNumber > vm.pagination.pageNumber) {
185+
// go to next
186+
getProducts(vm.pagination.nextPageInfo);
187+
vm.pagination.pageNumber = vm.pagination.pageNumber + 1;
188+
}
189+
else {
190+
// go to previous
191+
getProducts(vm.pagination.previousPageInfo);
192+
vm.pagination.pageNumber = vm.pagination.pageNumber - 1;
193+
}
194+
}
149195
}
150196

151197
angular.module("umbraco")

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/js/shopify.resource.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,17 @@
2929
$http.get(`${apiEndpoint}/ValidateAccessToken`),
3030
"Failed");
3131
},
32-
getProductsList: function() {
32+
getProductsList: function(pageInfo) {
3333
return umbRequestHelper.resourcePromise(
34-
$http.get(`${apiEndpoint}/GetList`), "Failed to get resource");
34+
$http.get(`${apiEndpoint}/GetList?pageInfo=${pageInfo}`), "Failed to get resource");
35+
},
36+
getProductsByIds: function (ids) {
37+
return umbRequestHelper.resourcePromise(
38+
$http.post(`${apiEndpoint}/GetListByIds`, { ids: ids }), "Failed to get resource");
39+
},
40+
getTotalPages: function () {
41+
return umbRequestHelper.resourcePromise(
42+
$http.get(`${apiEndpoint}/GetTotalPages`), "Failed to get resource");
3543
}
3644
};
3745
});

src/Umbraco.Cms.Integrations.Commerce.Shopify/App_Plugins/UmbracoCms.Integrations/Commerce/Shopify/views/productPickerOverlay.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@
7676
position="center">
7777
<localize key="content_listViewNoItems">There are no products in the list.</localize>
7878
</umb-empty-state>
79+
<!-- Pagination -->
80+
<div class="flex justify-center" ng-show="!vm.loading">
81+
<umb-pagination page-number="vm.pagination.pageNumber"
82+
total-pages="vm.pagination.totalPages"
83+
on-next="vm.nextPage"
84+
on-prev="vm.prevPage"
85+
on-change="vm.changePage"
86+
on-go-to-page="vm.goToPage">
87+
</umb-pagination>
88+
</div>
7989

8090
<!-- Min Max help messages-->
8191
<div class="umb-contentpicker__min-max-help" ng-if="(model.config.validationLimit.max > 1 || model.config.validationLimit.min > 0)">

src/Umbraco.Cms.Integrations.Commerce.Shopify/Constants.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ public static class Constants
99

1010
public const string ProductsApiEndpoint = "https://{0}.myshopify.com/admin/api/{1}/products.json";
1111

12+
public const string ProductsCountApiEndpoint = "https://{0}.myshopify.com/admin/api/{1}/products/count.json";
13+
14+
public const int DEFAULT_PAGE_SIZE = 10;
15+
1216
public static class RenderingComponent
1317
{
1418
public const string DefaultV8ViewPath = AppPluginFolderPath + "/Render/Products.cshtml";

src/Umbraco.Cms.Integrations.Commerce.Shopify/Controllers/ProductsController.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using Umbraco.Cms.Integrations.Commerce.Shopify.Configuration;
77

88
using static Umbraco.Cms.Integrations.Commerce.Shopify.ShopifyComposer;
9+
using System.Linq;
10+
911

1012
#if NETCOREAPP
1113
using Microsoft.Extensions.Options;
@@ -63,6 +65,17 @@ public async Task<string> GetAccessToken([FromBody] OAuthRequestDto authRequestD
6365
[HttpPost]
6466
public void RevokeAccessToken() => _apiService.RevokeAccessToken();
6567

66-
public async Task<ResponseDto<ProductsListDto>> GetList() => await _apiService.GetResults();
68+
public async Task<ResponseDto<ProductsListDto>> GetList(string pageInfo) => await _apiService.GetResults(pageInfo);
69+
70+
public async Task<ResponseDto<ProductsListDto>> GetListByIds([FromBody] RequestDto dto) =>
71+
await _apiService.GetProductsByIds(dto.Ids.Select(p => (long.Parse(p))).ToArray());
72+
73+
[HttpGet]
74+
public async Task<int> GetTotalPages()
75+
{
76+
var productsCount = await _apiService.GetCount();
77+
78+
return productsCount / Constants.DEFAULT_PAGE_SIZE + 1;
79+
}
6780
}
6881
}

src/Umbraco.Cms.Integrations.Commerce.Shopify/Editors/ShopifyProductPickerValueConverter.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,20 @@ public override object ConvertIntermediateToObject(IPublishedElement owner, IPub
4848
{
4949
if (inter == null) return null;
5050

51-
var ids = (long[]) inter;
51+
var ids = (long[])inter;
5252

53-
var t = Task.Run(async () => await _apiService.GetResults());
53+
var t = Task.Run(async () => await _apiService.GetProductsByIds(ids));
5454

5555
var result = t.Result;
5656

5757
var products = from p in result.Result.Products
58-
where ids.Contains(p.Id)
59-
select new ProductViewModel
60-
{
61-
Id = p.Id,
62-
Title = p.Title,
63-
Body = p.Body,
64-
Image = p.Image.Src
65-
};
58+
select new ProductViewModel
59+
{
60+
Id = p.Id,
61+
Title = p.Title,
62+
Body = p.Body,
63+
Image = p.Image?.Src
64+
};
6665

6766
return products.ToList();
6867
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net.Http;
4+
using System.Web;
5+
6+
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Helpers
7+
{
8+
public static class HttpResponseHelper
9+
{
10+
/// <summary>
11+
/// Retrieve Shopify page_info query string parameters used for pagination from forward/backwards operations.
12+
/// </summary>
13+
/// <param name="response"></param>
14+
/// <returns></returns>
15+
public static Tuple<string, string> GetPageInfo(this HttpResponseMessage response)
16+
{
17+
string previousPageInfo = string.Empty;
18+
string nextPageInfo = string.Empty;
19+
20+
if (!response.Headers.Contains("Link"))
21+
{
22+
return default;
23+
}
24+
25+
var linkHeader = response.Headers.GetValues("Link").FirstOrDefault();
26+
if (linkHeader != null && linkHeader.Contains("rel"))
27+
{
28+
if (linkHeader.Contains("previous") && linkHeader.Contains("next"))
29+
{
30+
var relArr = linkHeader.Split(',');
31+
foreach (var item in relArr)
32+
{
33+
var link = item.Split(';');
34+
var servicePageInfo = HttpUtility.ParseQueryString(link[0].Replace("<", string.Empty).Replace(">", string.Empty)).Get("page_info");
35+
if (link[1].Contains("previous"))
36+
{
37+
previousPageInfo = servicePageInfo;
38+
}
39+
else if (link[1].Contains("next"))
40+
{
41+
nextPageInfo = servicePageInfo;
42+
}
43+
}
44+
}
45+
else
46+
{
47+
var link = linkHeader.Split(';');
48+
var servicePageInfo = HttpUtility.ParseQueryString(link[0].Replace("<", string.Empty).Replace(">", string.Empty)).Get("page_info");
49+
if (link[1].Contains("previous"))
50+
{
51+
previousPageInfo = servicePageInfo;
52+
}
53+
else if (link[1].Contains("next"))
54+
{
55+
nextPageInfo = servicePageInfo;
56+
}
57+
}
58+
59+
}
60+
61+
return new Tuple<string, string>(previousPageInfo, nextPageInfo);
62+
}
63+
}
64+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Umbraco.Cms.Integrations.Commerce.Shopify.Models.Dtos
4+
{
5+
public class RequestDto
6+
{
7+
[JsonProperty("ids")]
8+
public string[] Ids { get; set; }
9+
}
10+
}

0 commit comments

Comments
 (0)