Repository: DestinyItemManager/DIM
Pull request #5297 (2020-06-13 07:26:52)

make images longpress-proof
this replaces a lot of adhoc `pointer-events:none;`
with a single global one affecting all BungieImage and AppIcon.

it omits onClick prop from BungieImage (AppIcon already doesn't have it) so that onClicks must be applied to a wrapper, and images serve a focused purpose of just displaying images rather than being interactive

this should prevent the need to individually find and prevent default mouseclick functions on images, like the "save as" prompt occasionally popping up on some mobile browsers


Patch:
diff --git a/src/app/App.m.scss b/src/app/App.m.scss
index 3e5776f0a..9fb32e65f 100644
--- a/src/app/App.m.scss
+++ b/src/app/App.m.scss
@@ -11,3 +11,7 @@
   height: 100%;
   box-sizing: border-box;
 }
+
+:global(.no-pointer-events) {
+  pointer-events: none;
+}
diff --git a/src/app/destiny1/loadout-builder/loadout-builder.scss b/src/app/destiny1/loadout-builder/loadout-builder.scss
index c1b557138..6660a272b 100644
--- a/src/app/destiny1/loadout-builder/loadout-builder.scss
+++ b/src/app/destiny1/loadout-builder/loadout-builder.scss
@@ -261,7 +261,6 @@
   }
 
   .vendor-icon-background {
-    pointer-events: none;
     background-color: rgba(100, 100, 100, 0.6);
     position: absolute;
     width: 20px;
diff --git a/src/app/dim-ui/BungieImage.tsx b/src/app/dim-ui/BungieImage.tsx
index 01e11376b..1404490ac 100644
--- a/src/app/dim-ui/BungieImage.tsx
+++ b/src/app/dim-ui/BungieImage.tsx
@@ -1,23 +1,28 @@
 import React from 'react';
+import clsx from 'clsx';
 
 /**
  * A relative path to a Bungie.net image asset.
  */
 export type BungieImagePath = string;
 
-interface BungieImageProps {
+export type BungieImageProps = Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'onClick'> & {
   src: BungieImagePath;
-}
+};
 
 /**
  * An image tag that links its src to bungie.net. Other props pass through to the underlying image.
  */
-export default React.memo(function BungieImage(
-  props: BungieImageProps & React.ImgHTMLAttributes<HTMLImageElement>
-) {
+export default React.memo(function BungieImage(props: BungieImageProps) {
   const { src, ...otherProps } = props;
-
-  return <img src={bungieNetPath(src)} loading="lazy" {...otherProps} />;
+  return (
+    <img
+      src={bungieNetPath(src)}
+      loading="lazy"
+      {...otherProps}
+      className={clsx(otherProps.className, 'no-pointer-events')}
+    />
+  );
 });
 
 /**
diff --git a/src/app/dim-ui/BungieImageAndAmmo.tsx b/src/app/dim-ui/BungieImageAndAmmo.tsx
index 72c24146c..b83c92a5c 100644
--- a/src/app/dim-ui/BungieImageAndAmmo.tsx
+++ b/src/app/dim-ui/BungieImageAndAmmo.tsx
@@ -1,20 +1,17 @@
 import React from 'react';
 import styles from './BungieImageAndAmmo.m.scss';
-import BungieImage, { BungieImagePath } from './BungieImage';
+import BungieImage, { BungieImageProps } from './BungieImage';
 import clsx from 'clsx';
 
-interface BungieImageProps {
-  src: BungieImagePath;
+type BungieImageAndAmmoProps = BungieImageProps & {
   hash: number;
-}
+};
 
 /**
  * A display for perk images that knows about certain items that have
  * corresponding ammo types.
  */
-export default function BungieImageAndAmmo(
-  props: BungieImageProps & React.ImgHTMLAttributes<HTMLImageElement>
-) {
+export default function BungieImageAndAmmo(props: BungieImageAndAmmoProps) {
   const { hash, className, ...otherProps } = props;
 
   let ammoImage;
diff --git a/src/app/inventory/InventoryItem.m.scss b/src/app/inventory/InventoryItem.m.scss
index 0e0d94d93..03420a064 100644
--- a/src/app/inventory/InventoryItem.m.scss
+++ b/src/app/inventory/InventoryItem.m.scss
@@ -35,7 +35,6 @@
   height: var(--item-size);
   box-sizing: border-box;
   border: $item-border-width solid #ddd;
-  pointer-events: none;
 
   &:focus {
     outline: none;
@@ -150,7 +149,6 @@
 
 // Individual icons in the icon tray
 .icon {
-  pointer-events: none;
   display: block;
   position: static;
   width: calc(var(--item-size) / 5);
diff --git a/src/app/loadout-builder/PerkPicker.tsx b/src/app/loadout-builder/PerkPicker.tsx
index 00b9bffae..6a2d1d775 100644
--- a/src/app/loadout-builder/PerkPicker.tsx
+++ b/src/app/loadout-builder/PerkPicker.tsx
@@ -512,13 +512,14 @@ function LockedItemIcon({
       );
     case 'perk':
       return (
-        <BungieImageAndAmmo
-          onClick={onClick}
-          className={styles.selectedPerk}
-          hash={lockedItem.perk.hash}
-          title={lockedItem.perk.displayProperties.name}
-          src={lockedItem.perk.displayProperties.icon}
-        />
+        <span onClick={onClick}>
+          <BungieImageAndAmmo
+            className={styles.selectedPerk}
+            hash={lockedItem.perk.hash}
+            title={lockedItem.perk.displayProperties.name}
+            src={lockedItem.perk.displayProperties.icon}
+          />
+        </span>
       );
     case 'burn':
       return (
diff --git a/src/app/organizer/ItemTable.m.scss b/src/app/organizer/ItemTable.m.scss
index c8ac20beb..e40ae1511 100644
--- a/src/app/organizer/ItemTable.m.scss
+++ b/src/app/organizer/ItemTable.m.scss
@@ -155,7 +155,6 @@ $toolbarHeight: 41px - 16px + 8px;
     height: calc(var(--item-size) * 0.75);
     display: block;
     box-sizing: border-box;
-    pointer-events: none;
     border: 1px solid #ddd;
   }
 }
diff --git a/src/app/shell/icons/AppIcon.tsx b/src/app/shell/icons/AppIcon.tsx
index 9e82de547..13f54e17f 100644
--- a/src/app/shell/icons/AppIcon.tsx
+++ b/src/app/shell/icons/AppIcon.tsx
@@ -21,7 +21,13 @@ export default React.memo(function AppIcon({
   if (typeof icon === 'string') {
     return (
       <span
-        className={clsx(icon, 'app-icon', className, spinning ? 'fa-spin' : false)}
+        className={clsx(
+          icon,
+          'app-icon',
+          'no-pointer-events',
+          className,
+          spinning ? 'fa-spin' : false
+        )}
         title={title}
       />
     );
diff --git a/src/app/vendors/VendorItem.m.scss b/src/app/vendors/VendorItem.m.scss
index 85519a216..1ea31659c 100644
--- a/src/app/vendors/VendorItem.m.scss
+++ b/src/app/vendors/VendorItem.m.scss
@@ -19,7 +19,6 @@
   position: absolute;
   top: $item-border-width + 2px;
   right: $item-border-width + 2px;
-  pointer-events: none;
 }
 
 .acquiredIcon {


Test patch:
diff --git a/src/app/dim-ui/BungieImage.test.tsx b/src/app/dim-ui/BungieImage.test.tsx
index 022437112..78827333a 100644
--- a/src/app/dim-ui/BungieImage.test.tsx
+++ b/src/app/dim-ui/BungieImage.test.tsx
@@ -6,6 +6,7 @@ test('bungie image prefixes with ', () => {
   const { container } = render(<BungieImage src="/foo.jpg" />);
   expect(container.firstChild).toMatchInlineSnapshot(`
     <img
+      class="no-pointer-events"
       loading="lazy"
       src="https://www.bungie.net/foo.jpg"
     />
diff --git a/src/app/dim-ui/BungieImageAndAmmo.test.tsx b/src/app/dim-ui/BungieImageAndAmmo.test.tsx
index 4fde251b7..a5b951b7d 100644
--- a/src/app/dim-ui/BungieImageAndAmmo.test.tsx
+++ b/src/app/dim-ui/BungieImageAndAmmo.test.tsx
@@ -11,6 +11,7 @@ it('should have an extra class name ', () => {
       class="test-class-name container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />
@@ -28,6 +29,7 @@ it('should not have an extra class name ', () => {
       class="container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />
@@ -45,6 +47,7 @@ it('should be a primary ammo image ', () => {
       class="container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />
@@ -62,6 +65,7 @@ it('should be a special ammo image ', () => {
       class="container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />
@@ -79,6 +83,7 @@ it('should be a heavy ammo image ', () => {
       class="container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />
@@ -96,6 +101,7 @@ it('should have no ammo image if hashes do not match ', () => {
       class="container"
     >
       <img
+        class="no-pointer-events"
         loading="lazy"
         src="https://www.bungie.net/foo.png"
       />