From 1c47cc33c4a34cc25d4e5378016ae2707990796d Mon Sep 17 00:00:00 2001
From: Yannick Lohse <contact@yannick-lohse.fr>
Date: Tue, 19 Nov 2019 17:31:50 +0100
Subject: [PATCH] feat: AppIcon doesn't require secure and domain props anymore

BREAKING CHANGE: From now on the AppIcon component needs to be inside a
cozy-client context
---
 react/AppIcon/Readme.md                       |  3 --
 react/AppIcon/index.jsx                       | 42 ++++++++++++-------
 react/AppIcon/test/AppIcon.spec.js            | 36 ++++++++--------
 .../__snapshots__/SmallAppItem.spec.jsx.snap  | 14 +++----
 4 files changed, 53 insertions(+), 42 deletions(-)

diff --git a/react/AppIcon/Readme.md b/react/AppIcon/Readme.md
index 906500a368..1c8e8a4d58 100644
--- a/react/AppIcon/Readme.md
+++ b/react/AppIcon/Readme.md
@@ -53,9 +53,6 @@ initialState = { fetchIcon: fetchIcon1 };
 </div>
 ```
 
-## `domain` and `secure` props
-If the `fetchIcon` is missing, the `<AppIcon />` component needs the `domain` prop (litteraly the cozy domain) and the secure prop, to know which protocol use between `http` or `https`.
-
 ### Provide `fallbackIcon`
 
 You can provide an `<Icon />` `icon` props to fallback when the AppIcon fetched is broken or the `fetchIcon` function errored.
diff --git a/react/AppIcon/index.jsx b/react/AppIcon/index.jsx
index 5a45194d38..e2370e113f 100644
--- a/react/AppIcon/index.jsx
+++ b/react/AppIcon/index.jsx
@@ -1,11 +1,11 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
 import cx from 'classnames'
+import { withClient } from 'cozy-client'
 import styles from './styles.styl'
 
-import Icon from '../Icon'
+import Icon, { iconPropType } from '../Icon'
 import palette from '../palette'
-import { iconPropType } from '../Icon'
 
 import { getPreloaded, preload } from './Preloader'
 import { AppDoctype } from '../proptypes'
@@ -17,15 +17,28 @@ const FETCHING = 'fetching'
 export class AppIcon extends Component {
   constructor(props, context) {
     super(props, context)
-    const { app, domain, secure } = props
-    const preloaded = getPreloaded(app, domain, secure)
-    this.state = {
-      error: null,
-      icon: preloaded,
-      status: preloaded ? DONE : FETCHING
-    }
     this.isUnmounting = false
     this.handleError = this.handleError.bind(this)
+
+    try {
+      const { app, client } = props
+      const cozyURL = new URL(client.getStackClient().uri)
+      // TODO: instead of storung domain and secure, pass down the client to the loaders (see https://github.com/cozy/cozy-ui/pull/1243#discussion_r348016827)
+      this.domain = cozyURL.host
+      this.secure = cozyURL.protocol === 'https:'
+
+      const preloaded = getPreloaded(app, this.domain, this.secure)
+      this.state = {
+        error: null,
+        icon: preloaded,
+        status: preloaded ? DONE : FETCHING
+      }
+    } catch (error) {
+      this.state = {
+        error,
+        status: ERRORED
+      }
+    }
   }
 
   componentWillUnmount() {
@@ -43,12 +56,12 @@ export class AppIcon extends Component {
   }
 
   async load() {
-    const { app, domain, fetchIcon, onReady, secure } = this.props
+    const { app, fetchIcon, onReady } = this.props
     const loadFn = fetchIcon || preload
     let loadedUrl
     let loadError
     try {
-      loadedUrl = await loadFn(app, domain, secure)
+      loadedUrl = await loadFn(app, this.domain, this.secure)
     } catch (error) {
       loadError = error
     }
@@ -119,10 +132,9 @@ AppIcon.propTypes = {
   fallbackIcon: iconPropType,
   /** Custom implementation of how to fetch icon */
   fetchIcon: PropTypes.func,
+  client: PropTypes.object.isRequired,
   className: PropTypes.string,
-  domain: PropTypes.string,
-  onReady: PropTypes.func,
-  secure: PropTypes.bool
+  onReady: PropTypes.func
 }
 
-export default AppIcon
+export default withClient(AppIcon)
diff --git a/react/AppIcon/test/AppIcon.spec.js b/react/AppIcon/test/AppIcon.spec.js
index b0b4b2eadb..311a58e715 100644
--- a/react/AppIcon/test/AppIcon.spec.js
+++ b/react/AppIcon/test/AppIcon.spec.js
@@ -4,13 +4,17 @@
 import React from 'react'
 import { shallow } from 'enzyme'
 
-import AppIcon from '../'
+import { AppIcon } from '../'
 
 describe('AppIcon component', () => {
   const app = {}
   const domain = 'cozy.tools'
   const secure = true
 
+  const mockClient = {
+    getStackClient: () => ({ uri: `https://${domain}` })
+  }
+
   let successFetchIcon
   let failureFetchIcon
 
@@ -31,12 +35,12 @@ describe('AppIcon component', () => {
 
   it(`renders as loading`, () => {
     const wrapper = shallow(
-      <AppIcon app={app} fetchIcon={successFetchIcon} secure={secure} />
+      <AppIcon app={app} fetchIcon={successFetchIcon} client={mockClient} />
     )
 
     const component = wrapper.getElement()
     expect(component).toMatchSnapshot()
-    expect(successFetchIcon).toHaveBeenCalledWith(app, undefined, secure)
+    expect(successFetchIcon).toHaveBeenCalledWith(app, domain, secure)
     expect(console.error).toHaveBeenCalledTimes(0)
   })
 
@@ -45,15 +49,15 @@ describe('AppIcon component', () => {
       <AppIcon
         app={app}
         fetchIcon={successFetchIcon}
+        client={mockClient}
         onReady={() => {
           wrapper.update()
           const component = wrapper.getElement()
           expect(component).toMatchSnapshot()
-          expect(successFetchIcon).toHaveBeenCalledWith(app, undefined, secure)
+          expect(successFetchIcon).toHaveBeenCalledWith(app, domain, secure)
           expect(console.error).toHaveBeenCalledTimes(0)
           done()
         }}
-        secure={secure}
       />
     )
   })
@@ -63,15 +67,15 @@ describe('AppIcon component', () => {
       <AppIcon
         app={app}
         fetchIcon={failureFetchIcon}
+        client={mockClient}
         onReady={() => {
           wrapper.update()
           const component = wrapper.getElement()
           expect(component).toMatchSnapshot()
-          expect(failureFetchIcon).toHaveBeenCalledWith(app, undefined, secure)
+          expect(failureFetchIcon).toHaveBeenCalledWith(app, domain, secure)
           expect(console.error).toHaveBeenCalledTimes(0)
           done()
         }}
-        secure={secure}
       />
     )
   })
@@ -81,15 +85,15 @@ describe('AppIcon component', () => {
       <AppIcon
         app={app}
         fetchIcon={failureFetchIcon}
+        client={mockClient}
         onReady={() => {
           wrapper.update()
           const component = wrapper.getElement()
           expect(component).toMatchSnapshot()
-          expect(failureFetchIcon).toHaveBeenCalledWith(app, undefined, secure)
+          expect(failureFetchIcon).toHaveBeenCalledWith(app, domain, secure)
           expect(console.error).toHaveBeenCalledTimes(0)
           done()
         }}
-        secure={secure}
         fallbackIcon="warning"
       />
     )
@@ -113,36 +117,35 @@ describe('AppIcon component', () => {
 
   it(`uses Preloader.preload when no fetchIcon method is provided`, async done => {
     jest.mock('../Preloader')
-    const AppIcon = require('../').default
+    const AppIcon = require('../').AppIcon
     const Preloader = require('../Preloader')
     const wrapper = shallow(
       <AppIcon
         app={app}
+        client={mockClient}
         onReady={() => {
           wrapper.update()
           const component = wrapper.getElement()
           expect(component).toMatchSnapshot()
-          expect(Preloader.preload).toHaveBeenCalledWith(app, undefined, secure)
+          expect(Preloader.preload).toHaveBeenCalledWith(app, domain, secure)
           expect(console.error).toHaveBeenCalledTimes(0)
           done()
         }}
-        secure={secure}
       />
     )
   })
 
   it(`renders immediately when icon is alredy preloaded`, async done => {
     jest.mock('../Preloader')
-    const AppIcon = require('../').default
+    const AppIcon = require('../').AppIcon
     const Preloader = require('../Preloader')
 
     shallow(
       <AppIcon
         app={app}
+        client={mockClient}
         onReady={() => {
-          const wrapper = shallow(
-            <AppIcon app={app} domain={domain} secure={secure} />
-          )
+          const wrapper = shallow(<AppIcon app={app} client={mockClient} />)
           const component = wrapper.getElement()
           expect(component).toMatchSnapshot()
           expect(Preloader.getPreloaded).toHaveBeenCalledWith(
@@ -153,7 +156,6 @@ describe('AppIcon component', () => {
           expect(console.error).toHaveBeenCalledTimes(0)
           done()
         }}
-        secure={secure}
       />
     )
   })
diff --git a/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap b/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap
index dd383ff3d5..70331303c8 100644
--- a/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap
+++ b/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap
@@ -9,7 +9,7 @@ exports[`SmallAppItem component should render correctly an app 1`] = `
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {
@@ -55,7 +55,7 @@ exports[`SmallAppItem component should render correctly an app in maintenance 1`
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {
@@ -110,7 +110,7 @@ exports[`SmallAppItem component should render correctly an app with iconToLoad (
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {
@@ -163,7 +163,7 @@ exports[`SmallAppItem component should render correctly an app with iconToLoad i
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {
@@ -216,7 +216,7 @@ exports[`SmallAppItem component should render correctly an app without developer
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "editor": "cozy",
@@ -254,7 +254,7 @@ exports[`SmallAppItem component should render correctly an installed app 1`] = `
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {
@@ -306,7 +306,7 @@ exports[`SmallAppItem component should render correctly an installed app without
   <div
     className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt"
   >
-    <AppIcon
+    <withClient(AppIcon)
       app={
         Object {
           "developer": Object {