electron-react-boilerplate: window.electron does not exist Error - electron

I'm using electron-react-boilerplate v4.0.0
I've exposed this electron api from preload.js to use in renderer process.
preload.js
The problem comes when trying use "window.electron.ipcRenderer.printTicket()" inside a react component.
Hello React Component
I have the Error: "window.electron does not exist on type 'Window & typeof globalThis'".

#Daniel,
Try this:-
Edit your index.tsx from something like this:-
import { render } from "react-dom";
import App from "./App";
render(<App />, document.getElementById("root"));
to something like this:-
import { render } from "react-dom";
import App from "./App";
declare global {
interface Window {
electron: any;
}
}
render(<App />, document.getElementById("root"));
Now you can use window.electron anywhere in the renderer. This fixed the problem for me. You could also add this in the App.js file.
Although I have not tested it, you can do something like this too:-
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
hworld: "Hello world" // window.electron.hworld is a string ("Hello world")
});
index.tsx
import { render } from "react-dom";
import App from "./App";
declare global {
interface Window {
electron: {
hworld: string // Since you know window.electron.hworld is a string
}
}
}
render(<App />, document.getElementById("root"));

Related

How to use quasar framework in vuepress 2?

I'm now using vuepress2 with quasar 2.7.1 like this:
import { Quasar } from 'quasar';
export default defineClientAppEnhance(({ app, router, siteData }) => {
app.use(Quasar);
}
#import url(https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons|Material+Icons+Outlined);
#import 'quasar/src/css/variables.sass';
#import 'quasar/src/css/core/colors.sass';
.quasar-comp {
#import 'quasar/src/css/index.sass';
}
/* I wrap the custome component in class .quasar-comp
so that the style from quasar won't conflict with style from vuepress. */
but there are 2 issues:
The style from quasar cannot works on some components, like q-btn-dropdown or q-menu.
It works well on dev mode(npm run docs:dev), but failed to build(npm run docs:build).
✔ Compiling with vite - done
✖ Rendering pages - failed
TypeError: Cannot convert undefined or null to object
at Function.assign (<anonymous>)
at installQuasar (/Users/lxm/Documents/neo/leaneo-docs/node_modules/quasar/dist/quasar.cjs.prod.js:6:15228)
at Object.install (/Users/lxm/Documents/neo/leaneo-docs/node_modules/quasar/dist/quasar.cjs.prod.js:6:479348)
at Object.use (/Users/lxm/Documents/neo/leaneo-docs/node_modules/#vue/runtime-core/dist/runtime-core.cjs.prod.js:3393:28)
at /Users/lxm/Documents/neo/leaneo-docs/docs/.vuepress/dist/.server/app.js:3745:7
at createVueApp (/Users/lxm/Documents/neo/leaneo-docs/docs/.vuepress/dist/.server/app.js:4177:11)
at async /Users/lxm/Documents/neo/leaneo-docs/node_modules/vuepress-vite/node_modules/#vuepress/bundler-vite/lib/build/build.js:49:52
at async /Users/lxm/Documents/neo/leaneo-docs/node_modules/#vuepress/utils/lib/withSpinner.js:12:24
at async build (/Users/lxm/Documents/neo/leaneo-docs/node_modules/vuepress-vite/node_modules/#vuepress/bundler-vite/lib/build/build.js:34:5)
at async /Users/lxm/Documents/neo/leaneo-docs/node_modules/#vuepress/cli/lib/commands/build/createBuild.js:51:5
Is there a better way to make quasar and vuepress works together?
Here is the implementation in Vuepress 2 (beta) and Quasar with Typescript.
First, install Quasar into Vuepress with the new syntax and import all styles:
client.ts:
import { defineClientConfig } from "#vuepress/client";
import { Quasar } from "quasar";
import 'quasar/src/css/index.sass';
export default defineClientConfig({
enhance({app, router, siteData}) {
app.use(Quasar);
},
setup() {},
rootComponents: [],
});
Then, ignore deprecation errors from sass if you're using vite.
config.ts
import { viteBundler } from '#vuepress/bundler-vite'
import { defineUserConfig } from '#vuepress/cli'
export default defineUserConfig({
// your config
...
bundler: viteBundler({
viteOptions: {
css: {
preprocessorOptions: {
scss: {
sassOptions: {
// ignore sass deprecation errors
quietDeps: true
}
}
}
}
},
}),
});
Hope it helps :)
Quasar needs some globales to be defined (ie if it's SSR). My following solution works ONYL on client-side so be sure to wrap your custom-quasar component in <ClientOnly> !
// .vuepress/config.ts
bundler: viteBundler({
viteOptions: {
define: {
__QUASAR_VERSION__: `'dev'`,
__QUASAR_SSR__: false,
__QUASAR_SSR_SERVER__: false,
__QUASAR_SSR_CLIENT__: false,
__QUASAR_SSR_PWA__: false
}
}
}),
// .vuepress/client.ts
import { defineClientConfig } from '#vuepress/client'
import Quasar from "quasar/src/install-quasar.js";
// optionally import your styles here
// import 'quasar/src/css/index.sass';
export default defineClientConfig({
enhance({ app, router, siteData }) {
app.use(Quasar);
},
setup() {
},
rootComponents: [],
});
<!-- README.md -->
<ClientOnly>
<MyComponent />
</ClientOnly>

Angular / Ionic mobile app ios does not fetch from Firebase using angularfire

I am trying to test a little Ionic/Angular sample app on an iOS Emulator.
On the web, all the requests to firestore using angularfire work perfectly fine.
Somehow if I try to execute the same app on the emulator, it keeps loading for the response of the request (if it was a empty response it would say that no results could be retrieved).
What is going on? Do i need to set something specifically for the Emulator to work and perform requests to Firestore?
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { Capacitor } from '#capacitor/core';
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
import { getAuth } from 'firebase/auth';
const firebaseApp = initializeApp({
apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId:
process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.VUE_APP_FIREBASE_APP_ID,
});
function whichAuth() {
let auth
if (Capacitor.isNativePlatform()) {
auth = initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
})
} else {
auth = getAuth()
}
return auth
}
export const auth = whichAuth()
const db = getFirestore();
export const auth = whichAuth();
export { firebaseApp, db };
Then in your component, cal your method like this await signInAnonymously(auth);. Don't forget to import the auth we exported at the top.
[Edit: updated with instructions Firebase JS SDK version 9 (modular)]
This error occurs because Firebase Auth incorrectly detects its environment as a normal browser environment and tries to load remote Google APIs, which results in the error you see in the console:
TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')
Fortunately, Firebase Auth already has logic to handle running in Cordova/Ionic apps, you just need to tell it which platform it's on.
For Firebase JS SDK version 9 (modular)
Simply import the Cordova Firebase Auth implementation:
import { getAuth } from 'firebase/auth';
For Firebase JS SDK <9 or the compatibility modules (auth/compat)
In capacitor.config set server: { iosScheme: "ionic" }:
// capacitor.config.json
{
"server": {
"iosScheme": "ionic"
}
}
There's a check in the auth/compat library here which, when it sees the URL scheme "ionic://", uses its Ionic/Cordova loading logic, and otherwise falls back to normal browser logic which fails with the error above.
Recent versions of Capacitor changed the URL scheme to "capacitor://" which fails this test but you can override it in your capacitor.config file (see the config option iosScheme).
(See also #alistairheath's comment here).
Been struggling a lot with this issue too but I managed to fix it. For those who need help here's my code.
You can delete all Firebase related imports from app.module.ts since this solution only uses Firebase.
The packages rxfire and #angular/fire can be removed from your package.json. The only dependency I have is "firebase": "^9.6.1".
I used observables for the getObject and list functions since that's what I'm used to and I didn't want to rewrite my original code.
import { Injectable } from '#angular/core';
import { Capacitor } from '#capacitor/core';
import { environment } from '#environment';
import { initializeApp } from 'firebase/app';
import { Auth, getAuth, indexedDBLocalPersistence, initializeAuth, signInWithCustomToken } from 'firebase/auth';
import { Database, getDatabase, onValue, orderByChild, query, ref } from 'firebase/database';
import { Observable, Observer, from } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class FirebaseService {
private readonly database: Database;
private readonly auth: Auth;
constructor() {
const firebaseApp = initializeApp(environment.firebase);
if (Capacitor.isNativePlatform()) {
initializeAuth(firebaseApp, {
persistence: indexedDBLocalPersistence
});
}
this.database = getDatabase(firebaseApp);
this.auth = getAuth(firebaseApp);
}
connectFirebase(firebaseToken) {
return from(signInWithCustomToken(this.auth, firebaseToken));
}
disconnectFirebase() {
return from(this.auth.signOut());
}
getObject<T>(path: string): Observable<T> {
return new Observable((observer: Observer<T>) => {
const dbRef = ref(this.database, path);
const listener = onValue(dbRef, snapshot => {
const data = snapshot.val();
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
public list<T>(path: string, orderChildBy?: string): Observable<Array<T>> {
return new Observable<Array<T>>((observer: Observer<Array<T>>) => {
const dbRef = ref(this.database, path);
const dbReference = !orderChildBy ? dbRef : query(dbRef, orderByChild(orderChildBy));
const listener = onValue(dbReference, snapshot => {
const data = Object.values<T>(snapshot.val() || {});
console.log(path, data);
observer.next(data);
});
return {
unsubscribe() {
listener();
}
};
});
}
}
For those who can't see the error message thrown by firebase try the following command in your Safari console to see the error.
window.location.reload()
The real problem: firebase-js-sdk on mobile iOS assumes google API (gapi) exists on the window, even when it isn't used.
I found a work around: Mock window.gapi before using firebase auth login:
window['gapi'] = {
load: (name: string) => Promise.resolve(),
iframes: {
getContext: () => {
return {
iframe: {
contentWindow: {
postMessage: (message: any) => {
console.log("gapi iframe message:", message);
}
}
}
}
}
}
} as any;

how to use react-leaflet-easyprint with react-leaflet 3

react-leaflet-easyprint has examples and docs for react-leaflet v1 and v2.
However, out of the box it seems to be incompatible with v3.
How can i make it work with v3?
This is how i did it.
I used the following packages instead and maybe they will work the same way
// package.json
"leaflet-easyprint": "^2.1.9",
"react-leaflet": "^4.0.0",
// MapPrint.js
import L from 'leaflet';
import 'leaflet-easyprint';
import { useEffect } from 'react';
import { useMap } from 'react-leaflet';
function MapPrint(props) {
const map = useMap();
useEffect(() => {
const control = L.easyPrint({
...props
});
map.addControl(control)
return () => {
map.removeControl(control);
}
}, [map]);
return null;
}
export default MapPrint
after that you could use it like this (inside MapContainer from the new react-leaflet):
<MapContainer zoom={3} >
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Print" />
<MapPrint position="topleft" sizeModes={['Current', 'A4Portrait', 'A4Landscape']} hideControlContainer={false} title="Export as PNG" exportOnly />
</MapContainer>
So what i am saying is to use the js implementation of easyprint instead of using react wrapped version and wrap it by yourself.

Notifications failing at notify()

I am trying to fire a simple notification in Quasar 2:
setup() {
const $q = useQuasar()
$q.notify('hello')
}
This fails with:
Uncaught TypeError: $q.notify is not a function
This is a UMD application that works fine without these two lines - I do not really know where to go from there as the docs say that there is nothing special to configure before using it.
Incindentally, my IDE is suggesting me notify() when typing $q. so at least at that level is it well recognized.
I think you forgot to add notify in plugins(quasar.conf.js).
return {
framework: {
plugins: [
'Notify'
],
}
}
For those using Vue CLI, you will need to work on quasar-user-options.js:
import { Notify } from "quasar";
// To be used on app.use(Quasar, { ... })
export default {
plugins: { Notify },
};
Quasar vite plugin + vue3
In main.ts or main.js, just add these 2 lines :
JS
import { Notify } from "quasar";
.use(Quasar, {
plugins: {
Notify,
}, // import Quasar plugins and add here
})
Here a example of my code :
JS
import { createApp } from 'vue'
import { Quasar } from 'quasar'
import quasarLang from 'quasar/lang/fr'
import { Notify } from "quasar";
import router from './router'
import { createPinia } from 'pinia'
import './style.css'
// Import icon libraries
import '#quasar/extras/material-icons/material-icons.css'
// Import Quasar css
import 'quasar/src/css/index.sass'
import App from './App.vue'
const pinia = createPinia()
createApp(App)
.use(Quasar, {
plugins: {
Notify,
}, // import Quasar plugins and add here
lang: quasarLang,
})
.use(router)
.use(pinia)
.mount('#app')

It shows black screen when trying to load Map on device with ionic 2 Google Map Native plugin

Note This project must be run on a device, iOS or Andriod
Background:
I had installed the native google maps plugin into my ionic 2 project using the command
$ ionic plugin add cordova-plugin-googlemaps --variable API_KEY_FOR_ANDROID="YOUR_ANDROID_API_KEY_IS_HERE" --variable API_KEY_FOR_IOS="YOUR_IOS_API_KEY_IS_HERE"
as shown in the following link: http://ionicframework.com/docs/v2/native/google-maps/
Then i'd run the command $ ionic platform add ios
and then $ ionic build ios
Everything goes as expected till this point.
When i try to display a map, i see a black screen, don't know whats missing!
Code:
/src/app/app.module.ts
import { NgModule, ErrorHandler } from '#angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}
/src/app/app.component.ts
import { Component } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar, Splashscreen } from 'ionic-native';
import { HomePage } from '../pages/home/home';
import {
GoogleMap,
GoogleMapsEvent,
GoogleMapsLatLng,
CameraPosition,
GoogleMapsMarkerOptions,
GoogleMapsMarker
// GoogleMapsMapTypeId
} from 'ionic-native';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage = HomePage;
constructor(platform: Platform) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
Splashscreen.hide();
let map = new MapPage();
map.loadMap();
});
}
}
class MapPage {
constructor() {}
// Load map only after view is initialize
ngAfterViewInit() {
this.loadMap();
}
loadMap() {
// make sure to create following structure in your view.html file
// and add a height (for example 100%) to it, else the map won't be visible
// <ion-content>
// <div #map id="map" style="height:100%;"></div>
// </ion-content>
// create a new map by passing HTMLElement
let element: HTMLElement = document.getElementById('map');
let map = new GoogleMap(element);
// create LatLng object
let ionic: GoogleMapsLatLng = new GoogleMapsLatLng(43.0741904,-89.3809802);
// create CameraPosition
let position: CameraPosition = {
target: ionic,
zoom: 18,
tilt: 30
};
// listen to MAP_READY event
map.one(GoogleMapsEvent.MAP_READY).then(() => {
// move the map's camera to position
map.moveCamera(position); // works on iOS and Android
});
// create new marker
let markerOptions: GoogleMapsMarkerOptions = {
position: ionic,
title: 'Ionic'
};
map.addMarker(markerOptions)
.then((marker: GoogleMapsMarker) => {
marker.showInfoWindow();
});
}
}
/src/pages/home/home.html
<ion-header>
<ion-navbar>
<ion-title>
Ionic Blank
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div #map id="map" style="height:100%;"></div>
</ion-content>
Could someone please help in figuring out the problem. Your help is deeply appreciated.
Link to documentation
Link to project repo
let map = new MapPage();
map.loadMap();`
MapPage is a plain typescript class. It is not a component and it wont run lifecycle hooks like this:
ngAfterViewInit() {
this.loadMap();
}
Also the call map.loadMap() in App.html is not working because your element is in home component.
I suggest you move map related code to home component.
Also use viewChild to get the map div which would be an ElementRef object.
Home Component
constructor() {}
#ViewChild('map')
private mapElement: ElementRef;
// Load map only after view is initialize
ngAfterViewInit() {
this.loadMap();
}
loadMap() {
let map = new GoogleMap(this.mapElement.nativeElement);
//...etc etc
}
}
Angular Documentation Reference: https://angular.io/docs/ts/latest/guide/
In app.scss, add:
ion-app._gmaps_cdv_ .nav-decor{
background: none !important;
}
Problem: Loading map on iOS platform was not working. It was showing black screen. However the map request was updating correctly on Google console.
Solution: I had to add the following code in .scss file -
ion-app._gmaps_cdv_ .nav-decor{
display: none !important;
}
This worked and loaded map correctly.
My solution was, do not initialize the map on the constructor, initialize it on the ionViewDidLoad.
ionViewDidLoad() {
console.log('ionViewDidLoad Maps');
setTimeout(()=>{
this.loadMap();
}, 1000)
}

Resources