Przeglądaj źródła

feat(admin-ui): Thumbnails make use of focal point data

Relates to #93
Michael Bromley 6 lat temu
rodzic
commit
667b8852c0

+ 3 - 3
packages/admin-ui/src/app/catalog/components/product-assets/product-assets.component.html

@@ -3,7 +3,7 @@
         <div class="featured-asset">
         <div class="featured-asset">
             <img
             <img
                 *ngIf="featuredAsset"
                 *ngIf="featuredAsset"
-                [src]="featuredAsset!.preview + '?preset=small'"
+                [src]="featuredAsset | assetPreview:'small'"
                 (click)="previewAsset(featuredAsset)"
                 (click)="previewAsset(featuredAsset)"
             />
             />
             <div class="placeholder" *ngIf="!featuredAsset">
             <div class="placeholder" *ngIf="!featuredAsset">
@@ -25,7 +25,7 @@
     <div class="featured-asset compact">
     <div class="featured-asset compact">
         <img
         <img
             *ngIf="featuredAsset"
             *ngIf="featuredAsset"
-            [src]="featuredAsset!.preview + '?preset=thumb'"
+            [src]="featuredAsset | assetPreview:'thumb'"
             (click)="previewAsset(featuredAsset)"
             (click)="previewAsset(featuredAsset)"
         />
         />
 
 
@@ -66,7 +66,7 @@
                     [title]=""
                     [title]=""
                     tabindex="0"
                     tabindex="0"
                 >
                 >
-                    <img [src]="asset.preview + '?preset=tiny'" />
+                    <img [src]="asset | assetPreview:'tiny'" />
                 </div>
                 </div>
                 <vdr-dropdown-menu vdrPosition="bottom-right">
                 <vdr-dropdown-menu vdrPosition="bottom-right">
                     <button type="button" vdrDropdownItem (click)="previewAsset(asset)">
                     <button type="button" vdrDropdownItem (click)="previewAsset(asset)">

+ 3 - 3
packages/admin-ui/src/app/catalog/components/product-list/product-list.component.html

@@ -51,11 +51,11 @@
                 <img
                 <img
                     *ngIf="
                     *ngIf="
                         groupByProduct
                         groupByProduct
-                            ? result.productPreview
-                            : result.productVariantPreview || result.productPreview as image;
+                            ? result.productAsset
+                            : result.productVariantAsset || result.productAsset as asset;
                         else imagePlaceholder
                         else imagePlaceholder
                     "
                     "
-                    [src]="image + '?preset=tiny'"
+                    [src]="asset | assetPreview:'tiny'"
                 />
                 />
                 <ng-template #imagePlaceholder>
                 <ng-template #imagePlaceholder>
                     <div class="placeholder"><clr-icon shape="image" size="48"></clr-icon></div>
                     <div class="placeholder"><clr-icon shape="image" size="48"></clr-icon></div>

+ 1 - 1
packages/admin-ui/src/app/catalog/components/product-variants-table/product-variants-table.component.html

@@ -15,7 +15,7 @@
                     <div class="featured-asset">
                     <div class="featured-asset">
                         <img
                         <img
                             *ngIf="variant.featuredAsset"
                             *ngIf="variant.featuredAsset"
-                            [src]="variant.featuredAsset!.preview + '?preset=tiny'"
+                            [src]="variant.featuredAsset | assetPreview:'tiny'"
                         />
                         />
                         <div class="placeholder" *ngIf="!variant.featuredAsset">
                         <div class="placeholder" *ngIf="!variant.featuredAsset">
                             <clr-icon shape="image" size="48"></clr-icon>
                             <clr-icon shape="image" size="48"></clr-icon>

+ 1 - 0
packages/admin-ui/src/app/catalog/providers/routing/asset-resolver.ts

@@ -23,6 +23,7 @@ export class AssetResolver extends BaseEntityResolver<Asset.Fragment> {
                 height: 0,
                 height: 0,
                 source: '',
                 source: '',
                 preview: '',
                 preview: '',
+                focalPoint: null,
             },
             },
             id => dataService.product.getAsset(id).mapStream(data => data.asset),
             id => dataService.product.getAsset(id).mapStream(data => data.asset),
         );
         );

+ 16 - 2
packages/admin-ui/src/app/common/generated-types.ts

@@ -3157,9 +3157,11 @@ export type SearchResult = {
   productId: Scalars['ID'],
   productId: Scalars['ID'],
   productName: Scalars['String'],
   productName: Scalars['String'],
   productPreview: Scalars['String'],
   productPreview: Scalars['String'],
+  productAsset?: Maybe<SearchResultAsset>,
   productVariantId: Scalars['ID'],
   productVariantId: Scalars['ID'],
   productVariantName: Scalars['String'],
   productVariantName: Scalars['String'],
   productVariantPreview: Scalars['String'],
   productVariantPreview: Scalars['String'],
+  productVariantAsset?: Maybe<SearchResultAsset>,
   price: SearchResultPrice,
   price: SearchResultPrice,
   priceWithTax: SearchResultPrice,
   priceWithTax: SearchResultPrice,
   currencyCode: CurrencyCode,
   currencyCode: CurrencyCode,
@@ -3172,6 +3174,13 @@ export type SearchResult = {
   score: Scalars['Float'],
   score: Scalars['Float'],
 };
 };
 
 
+export type SearchResultAsset = {
+  __typename?: 'SearchResultAsset',
+  id: Scalars['ID'],
+  preview: Scalars['String'],
+  focalPoint?: Maybe<Coordinate>,
+};
+
 /** The price of a search result product, either as a range or as a single price */
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 export type SearchResultPrice = PriceRange | SinglePrice;
 
 
@@ -4030,7 +4039,7 @@ export type AddNoteToOrderMutationVariables = {
 
 
 export type AddNoteToOrderMutation = ({ __typename?: 'Mutation' } & { addNoteToOrder: ({ __typename?: 'Order' } & Pick<Order, 'id'>) });
 export type AddNoteToOrderMutation = ({ __typename?: 'Mutation' } & { addNoteToOrder: ({ __typename?: 'Order' } & Pick<Order, 'id'>) });
 
 
-export type AssetFragment = ({ __typename?: 'Asset' } & Pick<Asset, 'id' | 'createdAt' | 'updatedAt' | 'name' | 'fileSize' | 'mimeType' | 'type' | 'preview' | 'source' | 'width' | 'height'>);
+export type AssetFragment = ({ __typename?: 'Asset' } & Pick<Asset, 'id' | 'createdAt' | 'updatedAt' | 'name' | 'fileSize' | 'mimeType' | 'type' | 'preview' | 'source' | 'width' | 'height'> & { focalPoint: Maybe<({ __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>)> });
 
 
 export type ProductVariantFragment = ({ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id' | 'createdAt' | 'updatedAt' | 'enabled' | 'languageCode' | 'name' | 'price' | 'currencyCode' | 'priceIncludesTax' | 'priceWithTax' | 'stockOnHand' | 'trackInventory' | 'sku'> & { taxRateApplied: ({ __typename?: 'TaxRate' } & Pick<TaxRate, 'id' | 'name' | 'value'>), taxCategory: ({ __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>), options: Array<({ __typename?: 'ProductOption' } & Pick<ProductOption, 'id' | 'code' | 'languageCode' | 'name' | 'groupId'> & { translations: Array<({ __typename?: 'ProductOptionTranslation' } & Pick<ProductOptionTranslation, 'id' | 'languageCode' | 'name'>)> })>, facetValues: Array<({ __typename?: 'FacetValue' } & Pick<FacetValue, 'id' | 'code' | 'name'> & { facet: ({ __typename?: 'Facet' } & Pick<Facet, 'id' | 'name'>) })>, featuredAsset: Maybe<({ __typename?: 'Asset' } & AssetFragment)>, assets: Array<({ __typename?: 'Asset' } & AssetFragment)>, translations: Array<({ __typename?: 'ProductVariantTranslation' } & Pick<ProductVariantTranslation, 'id' | 'languageCode' | 'name'>)> });
 export type ProductVariantFragment = ({ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id' | 'createdAt' | 'updatedAt' | 'enabled' | 'languageCode' | 'name' | 'price' | 'currencyCode' | 'priceIncludesTax' | 'priceWithTax' | 'stockOnHand' | 'trackInventory' | 'sku'> & { taxRateApplied: ({ __typename?: 'TaxRate' } & Pick<TaxRate, 'id' | 'name' | 'value'>), taxCategory: ({ __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>), options: Array<({ __typename?: 'ProductOption' } & Pick<ProductOption, 'id' | 'code' | 'languageCode' | 'name' | 'groupId'> & { translations: Array<({ __typename?: 'ProductOptionTranslation' } & Pick<ProductOptionTranslation, 'id' | 'languageCode' | 'name'>)> })>, facetValues: Array<({ __typename?: 'FacetValue' } & Pick<FacetValue, 'id' | 'code' | 'name'> & { facet: ({ __typename?: 'Facet' } & Pick<Facet, 'id' | 'name'>) })>, featuredAsset: Maybe<({ __typename?: 'Asset' } & AssetFragment)>, assets: Array<({ __typename?: 'Asset' } & AssetFragment)>, translations: Array<({ __typename?: 'ProductVariantTranslation' } & Pick<ProductVariantTranslation, 'id' | 'languageCode' | 'name'>)> });
 
 
@@ -4164,7 +4173,7 @@ export type SearchProductsQueryVariables = {
 };
 };
 
 
 
 
-export type SearchProductsQuery = ({ __typename?: 'Query' } & { search: ({ __typename?: 'SearchResponse' } & Pick<SearchResponse, 'totalItems'> & { items: Array<({ __typename?: 'SearchResult' } & Pick<SearchResult, 'enabled' | 'productId' | 'productName' | 'productPreview' | 'productVariantId' | 'productVariantName' | 'productVariantPreview' | 'sku' | 'channelIds'>)>, facetValues: Array<({ __typename?: 'FacetValueResult' } & Pick<FacetValueResult, 'count'> & { facetValue: ({ __typename?: 'FacetValue' } & Pick<FacetValue, 'id' | 'createdAt' | 'updatedAt' | 'name'> & { facet: ({ __typename?: 'Facet' } & Pick<Facet, 'id' | 'createdAt' | 'updatedAt' | 'name'>) }) })> }) });
+export type SearchProductsQuery = ({ __typename?: 'Query' } & { search: ({ __typename?: 'SearchResponse' } & Pick<SearchResponse, 'totalItems'> & { items: Array<({ __typename?: 'SearchResult' } & Pick<SearchResult, 'enabled' | 'productId' | 'productName' | 'productVariantId' | 'productVariantName' | 'sku' | 'channelIds'> & { productAsset: Maybe<({ __typename?: 'SearchResultAsset' } & Pick<SearchResultAsset, 'id' | 'preview'> & { focalPoint: Maybe<({ __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>)> })>, productVariantAsset: Maybe<({ __typename?: 'SearchResultAsset' } & Pick<SearchResultAsset, 'id' | 'preview'> & { focalPoint: Maybe<({ __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>)> })> })>, facetValues: Array<({ __typename?: 'FacetValueResult' } & Pick<FacetValueResult, 'count'> & { facetValue: ({ __typename?: 'FacetValue' } & Pick<FacetValue, 'id' | 'createdAt' | 'updatedAt' | 'name'> & { facet: ({ __typename?: 'Facet' } & Pick<Facet, 'id' | 'createdAt' | 'updatedAt' | 'name'>) }) })> }) });
 
 
 export type UpdateProductOptionMutationVariables = {
 export type UpdateProductOptionMutationVariables = {
   input: UpdateProductOptionInput
   input: UpdateProductOptionInput
@@ -5028,6 +5037,7 @@ export namespace AddNoteToOrder {
 
 
 export namespace Asset {
 export namespace Asset {
   export type Fragment = AssetFragment;
   export type Fragment = AssetFragment;
+  export type FocalPoint = (NonNullable<AssetFragment['focalPoint']>);
 }
 }
 
 
 export namespace ProductVariant {
 export namespace ProductVariant {
@@ -5177,6 +5187,10 @@ export namespace SearchProducts {
   export type Query = SearchProductsQuery;
   export type Query = SearchProductsQuery;
   export type Search = SearchProductsQuery['search'];
   export type Search = SearchProductsQuery['search'];
   export type Items = (NonNullable<SearchProductsQuery['search']['items'][0]>);
   export type Items = (NonNullable<SearchProductsQuery['search']['items'][0]>);
+  export type ProductAsset = (NonNullable<(NonNullable<SearchProductsQuery['search']['items'][0]>)['productAsset']>);
+  export type FocalPoint = (NonNullable<(NonNullable<(NonNullable<SearchProductsQuery['search']['items'][0]>)['productAsset']>)['focalPoint']>);
+  export type ProductVariantAsset = (NonNullable<(NonNullable<SearchProductsQuery['search']['items'][0]>)['productVariantAsset']>);
+  export type _FocalPoint = (NonNullable<(NonNullable<(NonNullable<SearchProductsQuery['search']['items'][0]>)['productVariantAsset']>)['focalPoint']>);
   export type FacetValues = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>);
   export type FacetValues = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>);
   export type FacetValue = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>)['facetValue'];
   export type FacetValue = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>)['facetValue'];
   export type Facet = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>)['facetValue']['facet'];
   export type Facet = (NonNullable<SearchProductsQuery['search']['facetValues'][0]>)['facetValue']['facet'];

+ 16 - 2
packages/admin-ui/src/app/data/definitions/product-definitions.ts

@@ -374,10 +374,24 @@ export const SEARCH_PRODUCTS = gql`
                 enabled
                 enabled
                 productId
                 productId
                 productName
                 productName
-                productPreview
+                productAsset {
+                    id
+                    preview
+                    focalPoint {
+                        x
+                        y
+                    }
+                }
                 productVariantId
                 productVariantId
                 productVariantName
                 productVariantName
-                productVariantPreview
+                productVariantAsset {
+                    id
+                    preview
+                    focalPoint {
+                        x
+                        y
+                    }
+                }
                 sku
                 sku
                 channelIds
                 channelIds
             }
             }

+ 1 - 1
packages/admin-ui/src/app/order/components/cancel-order-dialog/cancel-order-dialog.component.html

@@ -19,7 +19,7 @@
                 [class.is-cancelled]="line.quantity === 0"
                 [class.is-cancelled]="line.quantity === 0"
             >
             >
                 <td class="align-middle thumb">
                 <td class="align-middle thumb">
-                    <img [src]="line.featuredAsset.preview + '?preset=tiny'" />
+                    <img [src]="line.featuredAsset | assetPreview:'tiny'" />
                 </td>
                 </td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>

+ 1 - 1
packages/admin-ui/src/app/order/components/fulfill-order-dialog/fulfill-order-dialog.component.html

@@ -18,7 +18,7 @@
                 [class.ignore]="getUnfulfilledCount(line) === 0"
                 [class.ignore]="getUnfulfilledCount(line) === 0"
             >
             >
                 <td class="align-middle thumb">
                 <td class="align-middle thumb">
-                    <img *ngIf="line.featuredAsset" [src]="line.featuredAsset.preview + '?preset=tiny'" />
+                    <img *ngIf="line.featuredAsset" [src]="line.featuredAsset | assetPreview:'tiny'" />
                 </td>
                 </td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>

+ 1 - 1
packages/admin-ui/src/app/order/components/order-detail/order-detail.component.html

@@ -87,7 +87,7 @@
                     [class.is-cancelled]="line.quantity === 0"
                     [class.is-cancelled]="line.quantity === 0"
                 >
                 >
                     <td class="align-middle thumb">
                     <td class="align-middle thumb">
-                        <img *ngIf="line.featuredAsset" [src]="line.featuredAsset.preview + '?preset=tiny'" />
+                        <img *ngIf="line.featuredAsset" [src]="line.featuredAsset | assetPreview:'tiny'" />
                     </td>
                     </td>
                     <td class="align-middle name">{{ line.productVariant.name }}</td>
                     <td class="align-middle name">{{ line.productVariant.name }}</td>
                     <td class="align-middle sku">{{ line.productVariant.sku }}</td>
                     <td class="align-middle sku">{{ line.productVariant.sku }}</td>

+ 1 - 1
packages/admin-ui/src/app/order/components/refund-order-dialog/refund-order-dialog.component.html

@@ -16,7 +16,7 @@
             </thead>
             </thead>
             <tr *ngFor="let line of order.lines" class="order-line">
             <tr *ngFor="let line of order.lines" class="order-line">
                 <td class="align-middle thumb">
                 <td class="align-middle thumb">
-                    <img [src]="line.featuredAsset.preview + '?preset=tiny'" />
+                    <img [src]="line.featuredAsset | assetPreview:'tiny'" />
                 </td>
                 </td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle name">{{ line.productVariant.name }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>
                 <td class="align-middle sku">{{ line.productVariant.sku }}</td>

+ 1 - 1
packages/admin-ui/src/app/shared/components/asset-gallery/asset-gallery.component.html

@@ -7,7 +7,7 @@
     >
     >
         <div class="card-img">
         <div class="card-img">
             <div class="selected-checkbox"><clr-icon shape="check-circle" size="32"></clr-icon></div>
             <div class="selected-checkbox"><clr-icon shape="check-circle" size="32"></clr-icon></div>
-            <img [src]="asset.preview + '?preset=thumb'" />
+            <img [src]="asset | assetPreview:'thumb'" />
         </div>
         </div>
         <div class="detail">
         <div class="detail">
             <vdr-entity-info
             <vdr-entity-info

+ 1 - 1
packages/admin-ui/src/app/shared/components/asset-preview/asset-preview.component.html

@@ -8,7 +8,7 @@
             [editable]="settingFocalPoint"
             [editable]="settingFocalPoint"
             (focalPointChange)="onFocalPointChange($event)"
             (focalPointChange)="onFocalPointChange($event)"
         >
         >
-            <img class="" [src]="asset.preview + '?preset=' + size" #imageElement (load)="getDimensions()" />
+            <img class="asset-image" [src]="asset | assetPreview:size" #imageElement (load)="onImageLoad()" />
         </vdr-focal-point-control>
         </vdr-focal-point-control>
         <div class="focal-point-info" *ngIf="settingFocalPoint">
         <div class="focal-point-info" *ngIf="settingFocalPoint">
             <button class="icon-button" (click)="setFocalPointCancel()">
             <button class="icon-button" (click)="setFocalPointCancel()">

+ 7 - 2
packages/admin-ui/src/app/shared/components/asset-preview/asset-preview.component.ts

@@ -95,6 +95,11 @@ export class AssetPreviewComponent implements OnInit, OnDestroy {
         return parts[parts.length - 1];
         return parts[parts.length - 1];
     }
     }
 
 
+    onImageLoad() {
+        this.updateDimensions();
+        this.changeDetector.markForCheck();
+    }
+
     updateDimensions() {
     updateDimensions() {
         const img = this.imageElementRef.nativeElement;
         const img = this.imageElementRef.nativeElement;
         const container = this.previewDivRef.nativeElement;
         const container = this.previewDivRef.nativeElement;
@@ -142,7 +147,7 @@ export class AssetPreviewComponent implements OnInit, OnDestroy {
             .subscribe(
             .subscribe(
                 () => {
                 () => {
                     this.notificationService.success(_('asset.update-focal-point-success'));
                     this.notificationService.success(_('asset.update-focal-point-success'));
-                    this.asset.focalPoint = null;
+                    this.asset = { ...this.asset, focalPoint: null };
                     this.changeDetector.markForCheck();
                     this.changeDetector.markForCheck();
                 },
                 },
                 () => this.notificationService.error(_('asset.update-focal-point-error')),
                 () => this.notificationService.error(_('asset.update-focal-point-error')),
@@ -173,7 +178,7 @@ export class AssetPreviewComponent implements OnInit, OnDestroy {
                 .subscribe(
                 .subscribe(
                     () => {
                     () => {
                         this.notificationService.success(_('asset.update-focal-point-success'));
                         this.notificationService.success(_('asset.update-focal-point-success'));
-                        this.asset.focalPoint = { x, y };
+                        this.asset = { ...this.asset, focalPoint: { x, y } };
                         this.changeDetector.markForCheck();
                         this.changeDetector.markForCheck();
                     },
                     },
                     () => this.notificationService.error(_('asset.update-focal-point-error')),
                     () => this.notificationService.error(_('asset.update-focal-point-error')),

+ 16 - 0
packages/admin-ui/src/app/shared/pipes/asset-preview.pipe.ts

@@ -0,0 +1,16 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+import { Asset } from '../../common/generated-types';
+
+@Pipe({
+    name: 'assetPreview',
+})
+export class AssetPreviewPipe implements PipeTransform {
+    transform(asset: Asset, preset: string = 'thumb'): string {
+        if (!asset.preview || typeof asset.preview !== 'string') {
+            throw new Error(`Expected an Asset, got ${JSON.stringify(asset)}`);
+        }
+        const fp = asset.focalPoint ? `&fpx=${asset.focalPoint.x}&fpy=${asset.focalPoint.y}` : '';
+        return `${asset.preview}?preset=${preset}${fp}`;
+    }
+}

+ 1 - 0
packages/admin-ui/src/app/shared/shared-declarations.ts

@@ -11,6 +11,7 @@ export {
     AssetPreviewDialogComponent,
     AssetPreviewDialogComponent,
 } from './components/asset-preview-dialog/asset-preview-dialog.component';
 } from './components/asset-preview-dialog/asset-preview-dialog.component';
 export { AssetPreviewComponent } from './components/asset-preview/asset-preview.component';
 export { AssetPreviewComponent } from './components/asset-preview/asset-preview.component';
+export { AssetPreviewPipe } from './pipes/asset-preview.pipe';
 export { PercentageSuffixInputComponent } from './components/affixed-input/percentage-suffix-input.component';
 export { PercentageSuffixInputComponent } from './components/affixed-input/percentage-suffix-input.component';
 export {
 export {
     ChannelAssignmentControlComponent,
     ChannelAssignmentControlComponent,

+ 2 - 0
packages/admin-ui/src/app/shared/shared.module.ts

@@ -26,6 +26,7 @@ import {
     AssetPickerDialogComponent,
     AssetPickerDialogComponent,
     AssetPreviewComponent,
     AssetPreviewComponent,
     AssetPreviewDialogComponent,
     AssetPreviewDialogComponent,
+    AssetPreviewPipe,
     ChannelAssignmentControlComponent,
     ChannelAssignmentControlComponent,
     ChannelBadgeComponent,
     ChannelBadgeComponent,
     ChannelLabelPipe,
     ChannelLabelPipe,
@@ -152,6 +153,7 @@ const DECLARATIONS = [
     ExtensionHostComponent,
     ExtensionHostComponent,
     CustomFieldLabelPipe,
     CustomFieldLabelPipe,
     FocalPointControlComponent,
     FocalPointControlComponent,
+    AssetPreviewPipe,
 ];
 ];
 
 
 @NgModule({
 @NgModule({