0.0.1 • Published 4 years ago

sh-skyweather-fe v0.0.1

Weekly downloads
92
License
UNLICENSED
Repository
github
Last release
4 years ago

SkyWeather FE

PWA app for SkyWeather project.

BranchCircleCI
devCircleCI

.env file

API_PREFIX=https://7jxr6biy93.execute-api.ap-southeast-1.amazonaws.com/latest
API_KEY=IBM_API_KEY
SDK_API_KEY=SDK_API_KEY
GOOGLE_API_KEY=GOOGLE_API_KEY
ONE_SIGNAL_APP_ID=OBTAIN_THIS_FROM_ONE_SIGNAL
GTM_ENV=wt_MF-wCo9Sac4ftdGQgEw
GTM_PVW=env-65
ARCGIS_SUGGEST=https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/suggest
ARCGIS_CANDIDATE=https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates
AUTH0_CLIENT_ID=qPg6TqfLd7wqy0oMwp7X6nwRhYWAZDMh
AUTH0_DOMAIN=local-farmer-india.eu.auth0.com
AUTH0_AUDIENCE=http://localhost:3001
AUTH0_APP_ID=AUTH0_APP_ID
AUTH0_ENVIRONMENT=LOCAL
AUTH0_ENABLED_COUNTRIES=IN,KE

Commands

  • Setup yarn
  • Test + lint yarn test
  • Add new component/container yarn generate
  • (temp) Build && deploy yarn build; yarn deploy:s3; yarn deploy:cloudfront;
  • Reduce SVGs in folders yarn shrink-svg
  • Get Cloudfront invalidation ID aws cloudfront get-invalidation --distribution-id <CF distribution> --id <ID>
  • Running Ionic locally
    • Android yarn ionic:android
    • iOS ionic build

Routing

Look at app/containers/App/index.js

Utils - Stage 0

weather

Function nameParamsReturned Type
getRainLevelqpf: numberstring

geolocation

Function nameParamsReturned Type
getGeoPermissionNonePromise
getCurrentPositionoptions: objectPromise
getGeocode{  locationSelectorIdx: number,  isCurrentLocation: boolean,  start: function,  finish: function,  history: object}Promise

string

Function nameParamsReturned Type
getCamelCaseoriginalStr: stringstring

authentication

Function nameParamsReturned Type
getTokenNonestring
setTokenNonestring
removeTokenNonestring

dotenv files for different environments

  • Decided to go with deploying the same branch dev to all environments for the time being.
  • Use feature toggles and specific env variables in separate dotenv files in each environment so we could differentiate from within the environment:
if (process.env.DEV_ONLY_STUFF)
...

Bundle optimisation

  • Webpack has 2 aliases that swap existing dependencies to light-weight ones:
    • Preact (3Kb) to React (30Kb) - preact-compat layer that handles transformation of React to Preact. Read about limitations here. Not using for now, wait until closer to release date and revisit.
    • unfetch (0.5Kb) to whatwg-fetch (2.7Kb) - different fetch polyfills that do the same thing.
  • Assets like images and webfonts could be optimised later.

Styling

  • Stylesheet for now and Sass at some point when we need it.
  • Adopting BEM for CSS class naming convention to avoid polluting global scope.
    • Block - parent element
    • Element - child elements
    • Modifier - attributes specific to parent or children, meaning parents and children could have their own states. Take this markup below for different versions of parent component with different states of child elements.
<div class="component">
  <div class="component__child-element"></div>
</div>

<div class="component component--reversed">
  <div class="component--reversed__child-element--active"></div>
  <div class="component__child-element"></div>
</div>

We want to style parent with 'normal' and 'reversed' states, and style children of 'normal' and 'reversed' parents as well. Children may have an 'active' state that could look differently depending on their parents.

  • CSS
.component {
}

.component .component__child-element--reversed {
}

.component .component__child-element {
}

.component.component--reversed .component__child-element {
}

.component.component--reversed .component--reversed__child-element--active {
}
  • SASS
.component {
  $self: &;
  &--reversed {
    // here, $self gets evaluated to .component
    #{ $self }__child-element {
      &--active {
      }
    }
  }

  &__child-element {
    // matches .component--child-element but only if it is under .component--reversed by referencing parent selectors
    #{ $self }--reversed & {
    }
  }
}

Service worker in localhost

  • To allow Chrome to whitelist https://localhost:3000, run this in Mac
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/foo --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://localhost:3000
  • Add OfflinePlugin with extra entries for additional functionality like push.js

Google Tag Manager

Google Tag Manager (GTM) is loaded in app/index.html.

Environments:
  • Localhost: Localhost environment
  • Stage: Stage environment
  • Preprod: Preprod environment
  • Production: Live

Currently the deployments need to be done manually from the GTM console. Script versioning and CI/CD workflow pipeline to be fleshed out soon.

Tracking interactive elements:

For interactive elements such as divs or buttons with onClick props, GTM with GA is already configured to track click events as long as it has the following attributes:

AttributeWhat is it?Examples
data-analytics-captureTrigger that GTM will listen to, so this will not work without this attribute
data-event-categoryEvent category which is either a generic category type (e.g. navigation) or a container/page (e.g. locationBenefits) when the button event is tracking a specific data-event-action (e.g. save) in that container/page. Data attribute GTM will forward to GA to tracknavigation, errorRetry, share, cropSelection, locationBenefits, locationPage
data-event-actionEvent action which is either a generic action type (e.g. save) or if the data-event-category is a generic category type (e.g. share), it will be the specific attribute it's being performed on (e.g. description,${index}) i.e. share action on description on daily page at ${index} day. Data attribute GTM will forward to GA to trackclick, save, click-new-location-modal-confirm, click-new-location-modal-edit, click-new-location-modal-open, click-banner, description,${index}
data-event-labelEvent label is the specific attribute it's being performed on (e.g. /crop-selection) when data-event-action does not cover it. Otherwise default as Date.now(). Data attribute GTM will forward to GA to trackurl, selectedCropIds, Date.now()

Example:

<ActionButton
  data-analytics-capture
  data-event-category="locationBenefits"
  data-event-action="click"
  data-event-label="add-new-location"
  onClick={() => history.push('/new-location')}
/>

Validating that it works:

  1. Make sure you have GTM_ENV and GTM_PVW set in your .env file. You can find the values in 1password.
  2. Use GTM preview mode to ensure it's being tracked.
  3. Check Realtime > Events on FarmWeather - localhost on GA - this is because the Behaviour section on GA has processing delays that can be somewhere around 1 day delay (https://support.google.com/analytics/thread/11472446?hl=en)
Getting GTM to work with Ionic Capacitor:

Why GTM doesn't work out of the box with Ionic Capacitor: 1. When running the app on an android or iOS build, the protocol of the URI begins with capacitor i.e. capacitor://.... GTM by default only allows http or https protocols. 2. When running the app on an android or iOS build, you can not set or get cookies which GA uses by default to store the client id for GA.

Solution: 1. Create a new GTM variable called Empty function as a Custom Javascript type with the following contents:

function() {
     return function() {}
}
  1. Create a new GTM variable called JS - GetClientId as a Custom Javascript type with the following contents:
function() {
     if (window.Storage) {
       return window.localStorage.getItem('_clientId') || undefined;
     }
     return;
}
  1. Create a new GTM variable called JS - SetClientId as a Custom Javascript type with the following contents:
function() {
     return function() {
       if (window.Storage) {
          window.localStorage.setItem('_clientId', ga.getAll()[0].get('clientId'));
       }
     }
}
  1. Open your GTM variable where you set the Google Analytics Settings type. Go to More Settings > Fields to Set.
    1. Set Field Name to checkProtocolTask with Value as {{Empty function}}
    2. Set Field Name to storage with Value as none
    3. Set Field Name to hitCallback with Value as {{JS - SetClientId}}
    4. Set Field Name to clientId with Value as {{JS - GetClientId}}
  2. Publish, and you're done! (tip, check GA tracking ID variable for different environments)
React PWA Browser compatibility:

We have a script injection through GTM which spits out a custom html page depending on the user's browser type and browser version. To update this script snippet, login to GTM and make changes to Enforce Chrome Tag Injection tag. Then publish to the appropriate environment on GTM. Please check this confluence page to get the list of browser features supported in what version: https://yaradigitalfarming.atlassian.net/wiki/spaces/SDS/pages/895811595/Browser+Compatibility+Findings

Unit testing

React testing library

React testing library provides HOCs such as wait(), waitForElement() to handle asynchronous calls gracefully, Enzyme on the other hand, still hasn't had a decent solution to support React hooks. React testing library also uses actual DOM element for testing, which is more precise and easier to integrate with Cucumber.

Mocking

In order to have clear speration of behaviors and integrations for Cucumber implementation, we need to mock utility and API functions extensively.

When mocking for user modules, we should always create a directory __mocks__ under the module, for example if I have a folder structure as below:

and I want to mock getGeoCode(), I should create a directory __mocks__ under utils and a new file geolocation.js, which looks like this:

Inside the geolocation.js, I should mock the implementation:

const mock = jest.fn().mockImplemention(()=>({
    getGeoCode: Promise.resolve(something),
    ...
}));

export default mock;

Ionic Framework

Icons and Splash Screen

  • Cordova-res is used for our project to crop,resize and generate icon and splash screen assets.
  • Our icons and splash screen assets are stored in resources under; names icons.png and splash.png respectively.
  • To generate these assets for android and ios, run cordova res ios --skip-config --copy and cordova-res android --skip-config--copy

Other docs