I have finished my Flutter app and it is running fine on both mobile platforms, iOS and Android.
Now I am trying to run the web app on my local Chrome navigator. The app is running but the screens where a http request is needed is not working.
The app uses Firebase and Cloud Messaging. I have solved all Firebase related errors when executing the web app, but the http parts of the app are not getting any response from the web server.
Here you have the index.html file from my project:
<!DOCTYPE html>
<html>
<head>
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="proyecto_flutter">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<title>proyecto_flutter</title>
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" type="text/css" href="splash/style.css">
</head>
<body style="position: fixed; inset: 0px; overflow: hidden; padding: 0px; margin: 0px; user-select: none; touch-action: none; font: 14px sans-serif; color: red;">
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.9/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.6.9/firebase-analytics.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyAErdmk...",
authDomain: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
measurementId: "..."
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
<script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-messaging.js"></script>
</script>
<script>
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
// navigator.serviceWorker.register("/flutter_service_worker.js");
navigator.serviceWorker.register("/firebase-messaging-sw.js");
});
}
</script>
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
<picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light) or (prefers-color-scheme: no-preference)">
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" />
</picture>
</body>
</html>
I would like to make the web app working just like the mobile apps.
What am I missing?
EDIT
Screenshot from iOS app:
Screenshot for web
Related
I am adding tag manager to my next.js site (next version 12.0.4) and the docs here https://nextjs.org/docs/basic-features/script say that I can use next/script in the _document.js file.
When I follow the instructions I then get an error that points me to this page https://nextjs.org/docs/messages/no-script-in-document-page saying I cannot use next/script in the _document.js page, this page points back to the original page.
I'm confused, what is the correct implementation?
After having a dig around I came across this post:
Next 11 and adding Script tags not working. No scripts are rendered
Basically saying scripts where not working, which once I had ignored the warnings and tried in my solution I found also to be the case.
The solution to use next/head in my _app.js seems to work fine.
read the above comment by #juliomalves for the explanation as to why this is to be done in _app.js
import Script from "next/script";
const App = ({ Component, pageProps }) => (
<>
<Script
id="tagmanager-main"
strategy="afterInteractive"
async
src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_MEASUREMENT_ID}`}
></Script>
<Script
id="tagmanager-setup"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${process.env.NEXT_PUBLIC_MEASUREMENT_ID}');
`,
}}
/>
{/* eslint-disable-next-line react/jsx-props-no-​spreading */}
<Component {...pageProps} />
</>
);
export default App;
This was my _document.tsx, maybe there was something in there stopping it from working, but I am happy enough with the working solution above.
import * as React from 'react';
// eslint-disable-next-line #next/next/no-document-import-in-page
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '#emotion/server/create-instance';
// import theme from '../../styles/theme';
import createEmotionCache from '../lib/createEmotionCache';
// eslint-disable-next-line #next/next/no-script-in-document
import Script from 'next/script';
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
{/* PWA primary color */}
{/* <meta name="theme-color" content={theme.palette.primary.main} /> */}
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<Script
id="tagman"
strategy="afterInteractive"
async
src="https://www.googletagmanager.com/gtag/js?id=G-xxxxx"
></Script>
<Script
id="tagman-datalayer"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-xxxxx');
`,
}}
/>
<script
src={`https://maps.googleapis.com/maps/api/js?key=${
process.env.NEXT_PUBLIC_MAPS_API_KEY || ''
}&libraries=&v=weekly`}
async
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
const originalRenderPage = ctx.renderPage;
// You can consider sharing the same emotion cache between all the SSR requests to speed up performance.
// However, be aware that it can have global side effects.
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
// eslint-disable-next-line react/display-name, #typescript-eslint/no-explicit-any
enhanceApp: (App: any) => (props) =>
<App emotionCache={cache} {...props} />,
});
const initialProps = await Document.getInitialProps(ctx);
// This is important. It prevents emotion to render invalid HTML.
// See https://github.com/mui-org/material-ui/issues/26561#issuecomment-855286153
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
...emotionStyleTags,
],
};
};
Now I have a splash in my index.html:
<body>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<img src="img/splash2.png" class="center"/>
How can I change the body according to the current url? I want to show different splash images for different pages.
I have a problem with my script, I am using next and React in my pages / _app.js file I have the following code:
import axios from "axios";
import { $ } from "jquery";
import App from "next/app";
import Router from "next/router";
import { destroyCookie, parseCookies } from "nookies";
import Layout from "../components/_App/Layout";
import "../public/css/boot.css";
import "../public/css/icons.css";
import "../public/css/themes/style.css";
import "../public/jquery";
import "../public/scripts";
import baseUrl from "../utils/baseUrl";
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (!token) {
const isProtectedRoute =
ctx.pathname === "/profile" ||
ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/my-orders-history" ||
ctx.pathname === "/admin/users" ||
ctx.pathname === "/admin/dashboard";
if (isProtectedRoute) {
redirectUser(ctx, "/auth/login");
}
} else {
try {
const payload = { headers: { Authorization: token } };
const url = `${baseUrl}/api/account`;
const response = await axios.get(url, payload);
const user = response.data;
const isRoot = user.role == "root";
const isAdmin = user.role == "admin";
// if authenticated but not root or admin
const isNotPermitted =
!(isRoot || isAdmin) &&
(ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/admin/customers" ||
ctx.pathname === "/admin/orders" ||
ctx.pathname === "/admin/dashboard");
if (isNotPermitted) {
redirectUser(ctx, "/products");
}
pageProps.user = user;
} catch (error) {
// console.error("Error getting current user", error);
//invalid token
destroyCookie(ctx, "token");
redirectUser(ctx, "/auth/login");
}
}
return { pageProps };
}
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
require($)(window);
}
syncLogout = e => {
if (e.key === "logout") {
Router.push("/");
}
};
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
In my components/_App/Layouts.js tenho o seguinte codigo:
import React from "react";
import Head from "next/head";
import Footer from "./Footer";
import StaticHeader from "./StaticHeader";
const Layout = ({ children, user }) => {
return (
<React.Fragment>
<Head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WdpShoes | Home</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./public/css/icons.css" />
<link href="https://file.myfontastic.com/gMNiHf8HU5GG4r6y622k2N/icons.css" rel="stylesheet" />
<link rel="stylesheet" href="/css/boot.css" />
<link rel="stylesheet" href="/css/themes/style.css" />
<link rel="shortcut icon" href="/css/themes/logo/favicon.png" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</Head>
<body>
<StaticHeader user={user} />
{children}
<Footer />
<script src="/jquery.js"></script>
<script src="/scripts.js"></script>
<script src="/css/themes/script.js"></script>
</body>
</React.Fragment>
);
};
export default Layout;
In the Components that are in my Layout the jquery and javascript are working but in my components they are not
When I import my script into my pages / _app.js file I encounter the following error:
ReferenceError: $ is not defined
at Object../public/scripts.js (C:\Users\walter\Desktop\mystore.next\server\pages_app.js:5217:1)
I looked for some materials and I didn't find anything that could help me solve this problem that is preventing me from developing several projects
I know the purpose of the site is to answer questions but to better explain my problem I have the complete code at:
https://codesandbox.io/s/dawn-mountain-vme5e?file=/pages/_app.js
And in its current state it encounters the Internal Server Error due to the fact that the $ function is returning an undefined,Because my script and jquery are not working correctly in my components
To understand your issue, a little theory is needed. Next.js uses server-side rendering and static generation, which means the all code is initially run on the server. jQuery is a client-side library and it can't run on the server. Therefore, you need to ensure that all the jQuery code is executed only on the client side. You can do this by checking if the window object exists and only then executing jQuery. Also, you've forgotten to include jQuery in your scripts.js file.
Modify the
scripts.js file:
import $ from "jquery";
if (typeof window !== "undefined") {
$(function () {
// Rest of the code...
}
}
Next, modify the componentDidMount function in _app.js:
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
}
What I have
My _document file:
import NextDocument, { Head } from 'next/document';
const GTM_TRACKING_ID = 'GTM-ID';
class WebAppDocument extends NextDocument {
render() {
return (
<html lang="es">
<Head>
{ /* Global Tag Manager (gtm.js) */}
{ /* => load GTM scripts according to environment variable */ }
<script dangerouslySetInnerHTML={{ __html: `window.dataLayer = window.dataLayer || []` }} />
<script
dangerouslySetInnerHTML={{
__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${GTM_TRACKING_ID}');
`,
}}
/>
</Head>
</html>
);
}
}
My _app file:
import NextApp from 'next/app';
const pushEvent = ({ event, pagePath, pageTitle }) => {
window.dataLayer.push({
event,
pagePath,
pageTitle,
});
};
class WebApp extends NextApp {
componentDidMount() {
// push data to Google Tag Manager
pushEvent({
event: 'PageView',
pagePath: window.location.pathname + window.location.search + window.location.hash,
pageTitle: document.title,
});
}
}
What I want
I've seen the example and used Router.events.on('routeChangeComplete', url => dataLayer.push({})) to execute dataLayer. Is this the best approach? Or is it better to run the code to populate the dataLayer in componentDidMount.
I want to load GTM scripts according to environment variable. How could this be achieved?
Everything is in this url:
https://developers.facebook.com/tools/debug/sharing/?q=https%3A%2F%2Fweally.org%2Fentity%2F5dcac229156b765cb8c45148%2Fissue%2F5dcac2085b0a145d1f08d16e
If you go to the page facebook is testing and lookup the source code, the meta tags are present (they are even duplicated, but that's another issue)
https://weally.org/entity/5dcac229156b765cb8c45148/issue/5dcac2085b0a145d1f08d16e
here's my _document.js file : I tried to enforce the tags on the first load in ssr
import React from 'react';
import Document, {Head, Main, NextScript} from 'next/document';
import {ServerStyleSheets} from '#material-ui/styles';
import theme from '../src/theme';
import {getIssueImageSrc} from "../src/utils/util";
import DeveloperError from "../src/error/DeveloperError";
const ISSUE_URL_PREFIX = '/entity/';
function isIssuePath(path) {
return !path.startsWith(ISSUE_URL_PREFIX) && path.indexOf('/issue/') != -1
}
function getIssueIdFromPath(path) {
const startIndex = path.indexOf('/issue/') + '/issue/'.length
let otherParam = path.indexOf('/', startIndex);
if (otherParam == -1) otherParam = path.length
const issueId = path.substr(startIndex, otherParam - startIndex)
console.log("issue id for paht ", path, " is : ", issueId)
return issueId
}
class MyDocument extends Document {
render() {
return (<html lang="en">
<Head>
<meta charSet="utf-8"/>
{/* Use minimum-scale=1 to enable GPU rasterization */}
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
/>
{/* PWA primary color */}
<meta name="theme-color" content={theme.palette.primary.main}/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
{this.props.desc && <meta property="og:description" content={this.props.desc}/>}
{this.props.desc == null && <meta property="og:description"
content="Make your complaint about any company visible to the entire world on WeAlly.org. We can finally look at the problems companies have with their customers, complain on WeAlly and join the responsible citizens"/>}
{this.props.image && <meta property="og:description" content={this.props.image}/>}
{this.props.image == null && <meta property="og:image" content={'https://weally.org/static/images/fb_splash.jpg'}/>}
{this.props.title && <meta property="og:title" content={this.props.title}/>}
{this.props.title == null && <meta property="og:title" content="Allied together, our complaints are powerful"/>}
<link rel="icon" href="/static/images/favicon.ico"/>
<script language="JavaScript" type="text/javascript" src="/static/js/scripts.js">
</script>
<meta property="og:url" content={`https://weally.org`}/>
<meta property="og:type" content="website"/>
</Head>
<body>
<Main/>
<NextScript/>
</body>
</html>);
}
}
MyDocument.getInitialProps = async ctx => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
const metaTags = {}
if (!process.browser) {
const path = ctx.asPath
if (path.startsWith(ISSUE_URL_PREFIX)) {
try {
const issueId = getIssueIdFromPath(path)
const mongoose = require('mongoose')
const Complaint = mongoose.model('Complaint')
const complaint = await Complaint.findById(issueId)
metaTags.image = getIssueImageSrc(complaint)
metaTags.desc = complaint.desc
metaTags.title = complaint.title
} catch (e) {
console.log( "Error in meta data prefetching ", e )
}
}
}
const propsToReturn = {
...metaTags,
...initialProps, // Styles fragment is rendered after the app and page rendering finish.
styles: (<React.Fragment>
{initialProps.styles}
{sheets.getStyleElement()}
</React.Fragment>),
}
return propsToReturn
}
export default MyDocument;
I naturally double checked, that the tags are inserted by this code, and they are.
Any ideas please, facebook is always falling back to the "default values" that are not relevant at all for that kind of urls. Also notice that facebook debugger is complaining about og:image that he doesn't seem to see at all