# Whitelist App

## Running locally

The api is available in dockerized form, the scripts are in the top level `e2e_testing` folder.

#### Install docker

You'll need to install docker for mac (which is pretty painless).  Note it's 'docker ce', don't
get confused by the whole enterprise paid for version.

https://store.docker.com/editions/community/docker-ce-desktop-mac

Once that's installed type

`docker-compose -v`

from the command line to check it's all setup.

#### Launch API first time

To run the api the first time use:

```
cd e2e_testing
./launchapi-freshdb.sh
```

This will build the images, and run the script that seeds the data.
* Note you can run this script again get a completely fresh db.

It's easiest to launch a second terminal window for the other commands, and leave this one
running in the foreground.

If you want to check the dockerised api is up and working;

```
curl http://test:Test123@localhost:15000/api/dbcheck
```

Which should return some json including:

```
"db": "whitelist",
"name": "test"
```

#### Launch the app

Then launch the webpack dev server for the app, in a new terminal:

```
cd ./app
npm run start-docker-backend
```

This is normal `webpack -d`, only difference is it sets the environment so that the app points to the
locally running docker api (`localhost:15000`).

Navigate to `http://localhost:8080/` and you should see the login screen.

#### Tester Credentials

    test / Test123


#### Updating and Restarting the API

Shutting down the api can be done by simply ctrl+c in the terminal you launched it in.  Alternatively
running `docker-compose down` in the e2e_testing folder.

To run it again use:

```
cd e2e_testing
./launchapi.sh
```

This will also build the latest api, so if there's been any changes since your last
`git pull` they will be available.

#### Cleaning the db.

The data in the db will persist on disk so any changes to data (whitelisted artists,
source edits, will stay).  If you want to reset the db to the seed state, just run the
fresh-db script again `launchapi-freshdb.sh`

#### Caveats

Some things aren't configured in the local docker api, and won't really work properly,
known issues:
- you can't use the 'scout finder' in soundcloud, you can add artists, but it won't
  download scouts.  this is because the background process itself is a docker instance
  and I'm not running docker in docker or anything weird like that.
  in production it's a cloudwatch event, so a future state might actually use amazon
  directly.

- spotify api won't likely be configured.  There's some hassles with getting a valid
  refresh token into the image that I need to solve at some point.  Mainly this
  means you won't be able to add spotify sources manually or spider for them.  I can
  fix this, just let me know how big a problem it is.

There's probably more things as well...



## A few notes about architecture

- It's redux, including reselect.
- Any component that's 'connect'ed should be in /containers
- Otherwise they are 'dumb' and go in /components (my definition of dumb is not connected)
- connected components should ideally be used prop free and self closing.  eg. <MyComponent />
    - This means we know that nothing above the component is messing with it, and we
      can only worry about what is below
    - We also can be free to move it around easily.
    - I can also look at a component with one or more props and *know* that it's a dumb component.

- To reuse a connected component, I tend to create simple functions that wrap the connect call and
  configure the mapping functions. This style works for me as I can grok the lifecycle of the component.

    - eg. function configureFoo(aVariable) {
            const mapState = ... /* aVariable probably used in map state or map dispatch */

            return connect(mapState)(Foo)
          }
          const MyConnectedFoo = configureFoo('bar');

      ... and then somewhere further down:
      ... <MyConnected />

    - I prefer this style over something like: <ConfiguredFoo aVariable="bar" /> as I want only
      dumb components to be
- Avoid using spread for props as much as possible. I occasionally do it when I'm rushed or
  lazy and it always makes it harder to figure out the component usage later.
- PureComponent has started to make it's way in, as the app has grown and the rendering of certain pages
  is problematic.  PureComponent is fairly straightforward if you use reselect.  So use it!  There's also
  a simple 'statelessPure' higher order component that expects a pure function and will wrap it in a
  PureComponent (since React still doesn't optimise stateless pure functions, and we really want to use
  them when we can.
- It's worth noting that PureComponent wont' always be much faster (or even faster at all), since the prop
  comparison might actually be slower than executing the function and virtual dom diffing.  It's not clear
  when this is, but since PureComponents can occasionally cause missed render bugs, I prefer not to go crazy
  using them everywhere.
- react router is there but a few principles:
    - hashrouter is preferred. We know the route is part of the client, nothing to do with the 'server'
      (this single page can and should be able to live anywhere). Advice about preferring HistoryRouter
      is wrong.
    - avoid <Redirect /> as much as possible (it really destroys the functional nature of react imo)  needing
      'redirects' in a single page app kind of suggest your doing them wrong.
    - especially avoid authentication related routing, (if the user is not authed, an overlay is rendered.
    - this avoids any weird redirect loops, and means we don't get stuck on /login page.  Redirecting to / from
      login is another sign we aren't thinking about a single page app correctly.
- StyledComponents is great, but I tend to create a few and use nested rules more. It avoids having to create
    - so many named consts.  I also try to name a styled component as either StyledDiv" or Styled if its at the root
      of the component.  That was I know at a glance I'm only looking at a style declaration.  It can be frustrating
      to find the logical components when they look the same as simple styled ones.
    - it's important to not go too deep with the nested rules though, as otherwise it can be a nightmare to figure out
      where the style of an element is coming from.
- A pattern for a connected component that reads ok is:
```jsx

  const Styled = styled.div`
    /* top level styles *
    .subComponent {
      /* and styles for the sub component */
    }
  `;

  const hoc = connect(/* map state and dispatch for the below */)

  const Component = statelessPure(({...}) => {
    return <Styled>...</Styled>;
  });

  export const ExportedComponent = hoc(Component);

```
- I'm not shy about connecting components.

- The store has three top lovel keys:
  - ui, domain, user
  - (there are some legacy keys too unfortunately)
  - the domain shuold look a bit like a db, objects {}
  - the ui should hold references into the domain, eg. selectedArtistId: 6
  - reselect selectors should be created when a component needs to go from a ui reference to the object
     - they can be reused elseehwere
     - they make pure components tractable
  - reducers are not a 1-1 mapping to actions
     - this works ok, but can feel a bit spaghetti

- the artist page is designed to render s much of the artist object as we have, might only be the name
  until the rest is fetched.  This keeps it feeling snappy, and is relatively straightforward with the
  store.domain / reselect pattern

- Mounting vs. componentDidUpdate...
  - I'm starting to think having more components that are always mounted at top level, that know when to render themselves,
    and then can fetch data only in componentDidUpdate, is slightly easier to reason about.
  - alternative is 'componentDidMount' only.
  - I think if you have *both* fetching in componentDidMount and componentDidUpdate it's tricky.
  - I think you only get this if you are using a container component that's mounted in <Switch >
    that also uses match params to fetch data.
  - I think there's an even cleaner container component pattern to be extracted.
