react-everywhere / re-start

react-native template to target multiple platforms :globe_with_meridians: :iphone: :computer: with single codebase.
https://medium.com/@amoghbanta/write-once-use-everywhere-with-react-native-d6e575efe58e#.nfl50gwfg
MIT License
1.3k stars 85 forks source link

Electron issues #73

Open thorakmedichi opened 5 years ago

thorakmedichi commented 5 years ago

Step 2: Describe your environment

Step 3: Describe the problem (try to include screenshots):

Running yarn electron-builder produces my AppImage but the screen is blank Running yarn electron-pack (yarn build -c.extraMetadata.main=build/electron.js) errors out with a ton of errors. Top of the list is

Application entry file "build/electron.js" in the "/home/thorakmedichi/Development/Projects/stormfree/day-tracer/dist/linux-unpacked/resources/app.asar" does not exist. Seems like a wrong configuration.
    at error (/home/thorakmedichi/Development/Projects/stormfree/day-tracer/node_modules/app-builder-lib/out/asar/asarFileChecker.js:44:14)

Am I missing something not outlined in your documentation?

Expected Results:

An electron app that works, dev or production.

package.json

{
  "name": "testing",
  "version": "0.1.12",
  "author": "thorakmedichi",
  "main": "./index.electron.js",
  "homepage": "./",
  "scripts": {
    "android": "./node_modules/.bin/react-native run-android",
    "ios": "./node_modules/.bin/react-native run-ios",
    "web": "node scripts/start.js",
    "build": "node scripts/build.js",
    "lint": "eslint . --ext .js",
    "electron-dev": "concurrently \"BROWSER=none yarn react-scripts start\" \"wait-on http://localhost:3000 && electron .\"",
    "electron-pack": "build -c.extraMetadata.main=build/electron.js",
    "electron-build": "electron-builder",
    "preelectron-pack": "yarn build",
   },
  "build": {
    "appId": "com.example.electron-cra",
    "files": [
      "build/**/*",
      "node_modules/**/*",
      "public/*",
      "./index.electron.js"
    ],
    "directories": {
      "buildResources": "assets"
    }
  },

  ....etc
thorakmedichi commented 5 years ago

Resolved pack errors by changing

"electron-pack": "build -c.extraMetadata.main=build/electron.js",

to

"electron-pack": "build -c.extraMetadata.main=./index.electron.js",

Screen still blank.

Upon opening the build folder and running the index.html file locally it also is a blank screen. No errors showing in the dev console.

thorakmedichi commented 5 years ago

So my white screen appears to have been an issue with react-router

It would appear that BrowserHistory wont work with electron so I had to change to HashHistory

piranna commented 5 years ago

Can you provide a pull-request with your changes to have a record of them?

thorakmedichi commented 5 years ago

Can you provide a pull-request with your changes to have a record of them?

To be honest I don't know how this repo is put together to even decide where / how to make the PR for you. I just did a react-native init <Your Project Name> --template re-start and fixed from there.

I had to fix jest issues, webpack issues, package.json issues, index.electron.js issues etc AFTER that init.

I am happy to outline what we did though for the electron side if you want to add the PR yourself.

In short the yarn electron that your readme.md shows only loaded a loader, then we had to drag the index.electron.js file into the app loader... and then it only worked for local testing. Zero use in the real world where you need to build to give to other people without dev environments.

In package.json we added these scripts:

"build": "node scripts/build.js",
"electron-build:linux": "yarn build && electron-builder -l",
"electron-build:windows": "yarn build && electron-builder -w",
"electron-build:mac": "yarn build && electron-builder -m",

Then we added the "build" to package.json

"build": {
    "appId": "com.example.electron-cra",
    "files": [
      "dist/**/*",
      "build/**/*",
      "node_modules/**/*",
      "src/assets/**/*",
      "public/*",
      "./index.electron.js"
    ],
    "directories": {
      "buildResources": "./src/common/assets"
    },
    "win": {
      "icon": "./src/common/assets/app-icons/png/512x512.png"
    },
    "linux": {
      "icon": "./src/common/assets/app-icons/png/512x512.png"
    },
    "mac": {
      "icon": "./src/common/assets/app-icons/png/osx-icon-512x512.png"
    }
  },

Then we changed index.electron.js line 24 which was:

mainWindow.loadURL('http://localhost:3000');

To be

mainWindow.loadURL(`file://${path.join(__dirname, './build/index.html')}`);

Basically the following package.json command did nothing but cause issues so we just abandoned it all together

"electron-pack": "build -c.extraMetadata.main=./index.electron.js",

There was some issue in doing the electron build where it didn't like the assets folder outside of the /src dir so we moved them into /src/common/assets to share between all platforms.

We did a lot more than that but that is the core of the electron fixes we needed to do. I just cant remember every thing that was done as a whole for us to make re-start work for our specific use case

thorakmedichi commented 5 years ago

For our router issues - well we use react-navigation for RN as it has better animations and router handling than react-native-router. Basically our entire team thought react-native-router was kinda crap compared to alternatives.

For web we used react-router because its solid for web. To accomplish this we had to build some common interfaces and take advantage of the .web.js notation.

The trick was that RNW didn't like react-router's BrowserHistory for Electron builds so we had to use HashHistory instead.

So we basically had navigator.web.js

import { history } from './history';

/**
 * Used with navigator.js to make a universal command to navigate from components
 * This makes our main code platform agnostic
 *
 * @param {string} routeName The screen to navigate to
 * @param {object} params Any extra data you want to pass to the route
 */
const navigate = (routeName, params = {}) => {
    history.push({
        pathname: routeName,
        state: params
    });
};

const currentRoute = () => history.location.pathname.replace(/\//, '');

const closeDrawer = () => null;

export default {
    navigate,
    currentRoute,
    closeDrawer
};

and navigator.js

import { NavigationActions, DrawerActions } from 'react-navigation';

let navigator;

/**
 * Creates a ref callback to the App.js AppStack
 * @param {string} navigatorRef
 */
const setTopLevelNavigator = (navigatorRef) => {
    navigator = navigatorRef;
}

/**
 * Gets the current screen from react-navigation
 * @param {object} navigationState
 * @returns {*}
 */
const getActiveRouteName = (navigationState) => {
    if (isEmpty(navigationState)) {
        return null;
    }
    const route = navigationState.routes[navigationState.index];
    // dive into nested navigators
    if (route.routes) {
        return getActiveRouteName(route);
    }
    return route.routeName;
};

/**
 * Used with navigator.web.js to make a universal command to navigate from components
 * This makes our main code platform agnostic
 *
 * @param {string} routeName The screen to navigate to
 * @param {object} params Any extra data you want to pass to the route
 */
const navigate = (routeName, params) => {
    navigator.dispatch(
        NavigationActions.navigate({
            routeName,
            params,
        })
    );
}

const currentRoute = () => {
    if (navigator) {
        return getActiveRouteName(navigator.state.nav);
    }

    return null;
}

const closeDrawer = () =>  navigator.dispatch(DrawerActions.closeDrawer());

export default {
    navigate,
    currentRoute,
    setTopLevelNavigator,
    closeDrawer
};

history.js

/**
 * Creates a global history object for navigating web routes
 */
// BrowserHistory does not work with Electron so we have to use HashHistory
import createHashHistory from 'history/createHashHistory';

export const history = createHashHistory();

and our router.web.js file had

import { history } from './history';

<Router history={history}>
//.....
</Router>

Then anywhere in our app we could just do things like NavigationService.navigate('MyScreen') and regardless of the platform it knew what navigation system to use.

thorakmedichi commented 5 years ago

For our jest testing we found that the tests would lock up on us all the time so we changed package.json to be

"test": "npm run test:native && npm run test:web",
"test:web": "node scripts/test.js --config \"./config/web.jest.config.js\" --detectOpenHandles --maxWorkers=10",
"test:native": "node scripts/test.js --config \"./config/native.jest.config.js\" --ci --coverage --detectOpenHandles --maxWorkers=10",

Note the --detectOpenHandles --maxWorkers=10

thorakmedichi commented 5 years ago

Also we use react-native-elements (as I think a lot of people do) so we had to add the following to the webpack.config.xxxxx.js to the include below line 285

// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript and some ESnext features.
`${paths.appNodeModules}/react-native-elements`,
`${paths.appNodeModules}/react-native-vector-icons`,
`${paths.appNodeModules}/react-native-ratings`,
`${paths.appNodeModules}/react-native-status-bar-height`,

We also had to add the following to src/index.js

import iconFont from 'react-native-vector-icons/Fonts/MaterialIcons.ttf';

const iconFontStyles = `
    @font-face {
        src: url(${iconFont}) format('truetype');
        font-family: MaterialIcons;
    }
`;

// Create stylesheet
const style = document.createElement('style');
style.type = 'text/css';

if (style.styleSheet) {
    style.styleSheet.cssText = iconFontStyles;
} else {
    style.appendChild(document.createTextNode(iconFontStyles));
}

// Inject stylesheet
document.head.appendChild(style);

There was more... but that does the broad sweep of changes to basically get us running.

thorakmedichi commented 5 years ago

Oh and one more... we found we had to change babel.config.js to

module.exports = function (api) {
    api.cache(true);
    return {
        presets: [['module:metro-react-native-babel-preset'], ['react-app']],
        ignore: ['node_modules/art/core/color.js'],
        plugins: [
            ['module-resolver', {
                'alias': {
                    '^react-native$': 'react-native-web'
                }
            }]
        ],
    };
};
piranna commented 5 years ago

The re-start template is obsolete, the new build process is to install the re-base template and all the customized ones on top of it, and after that exec the finish-install script.

piranna commented 5 years ago

@amoghbanta can you provide me permissions on npm to fix that? I've already done a script to publish all new templates at once.

amoghbanta commented 5 years ago

@piranna, done! You have the access now. 😄

piranna commented 5 years ago

Is it still give me login errors...

403 Forbidden - PUT https://registry.npmjs.org/react-native-template-re-base - You do not have permission to publish "react-native-template-re-base". Are you logged in as the correct user?

I'm logged in npm as piranna.

thorakmedichi commented 5 years ago

The re-start template is obsolete, the new build process is to install the re-base template and all the customized ones on top of it, and after that exec the finish-install script.

okay good to know. We started this project about 1.5 months - 2 months ago and the readme.md that I see here now today is not what it was when we first found your library.

Cheers

amoghbanta commented 5 years ago

Is it still give me login errors...

403 Forbidden - PUT https://registry.npmjs.org/react-native-template-re-base - You do not have permission to publish "react-native-template-re-base". Are you logged in as the correct user?

I'm logged in npm as piranna.

Hey, @piranna, I just checked, you have admin rights on react-everywhere npm packages. If you could please check once again at your site, that'd be great.

piranna commented 5 years ago

NOW it worked :tada: All published as 0.4.1 version, thanks! :-D