404 page in nextjs app dir constantly refreshes with middleware - next.js

within my project, we use the nextjs app directory
however with that, it does not include root 404 pages, so we instead use the pages/404.tsx file. in our middleware, it uses next-auth and vercel edge config to send the user to a maintenance page (if enabled on edge config) and to check for auth pages and such, but even with everything commented out, it makes the 404 page constantly refresh itself
middleware.ts:
import { getToken } from "next-auth/jwt";
import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { get } from "#vercel/edge-config";
export const config = {
matcher: ["/((?!_next|icon.svg|favicon.ico|api).*)"]
};
export default withAuth(
async function middleware(req: NextRequest) {
// if (process.env.EDGE_CONFIG && process.env.NODE_ENV !== "development") {
// if (req.nextUrl.pathname.startsWith("/maintenance")) return null;
// const isInMaintenanceMode = await get("isInMaintenanceMode");
// console.log("isInMaintenanceMode", isInMaintenanceMode);
// if (isInMaintenanceMode) {
// return NextResponse.redirect(new URL("/maintenance", req.url));
// }
// }
// if (req.nextUrl.pathname.startsWith("/dashboard") || req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/register")) {
// console.log("matches")
// const token = await getToken({ req });
// const isAuth = !!token;
// const isAuthPage = req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/register");
// if (isAuthPage) {
// if (isAuth) {
// return NextResponse.redirect(new URL("/dashboard", req.url));
// }
// return null;
// }
// if (!isAuth) {
// let from = req.nextUrl.pathname;
// if (req.nextUrl.search) {
// from += req.nextUrl.search;
// }
// return NextResponse.redirect(new URL(`/login?callbackUrl=${encodeURIComponent(from)}`, req.url));
// }
// }
},
{
callbacks: {
async authorized() {
return true;
}
}
}
);
i have tried to comment out the middleware (which had no effect on the error)
and i have also made the matcher only target one page, which worked, however i need it to target all pages so that the middleware function works

Related

getStatic Path not working for base URL "/" in NextJS

I'm using Prismic and NextJS for the first time.
What I'm trying to is make it so when the user goes to the base url for the page localhost:3000/ in dev something will load. /About and /Pricing are working fine the base url doesn't work.
import { GetStaticPaths, GetStaticProps } from 'next'
import { SliceZone } from '#prismicio/react'
import * as prismicH from "#prismicio/helpers";
import { createClient, linkResolver } from '../../prismicio'
import { components } from '../../slices'
interface HomePageProps {
page: any
}
const HomePage = (props:HomePageProps) => {
return <SliceZone slices={props.page.data.slices} components={components} />
}
export default HomePage
interface HomePageStaticProps {
params: any
previewData:any
}
export async function getStaticProps(props:HomePageStaticProps) {
console.log("DOES NOT FIRE FOR localhost:3000")
const client = createClient({ previewData:props.previewData })
const params = props.params;
const uid = params?.pagePath?.[params.pagePath.length - 1] || "home";
const page = await client.getByUID("page", uid);
return {
props: {
page,
},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
const client = createClient();
const pages = await client.getAllByType("page");
const paths = pages.map((page) => prismicH.asLink(page, linkResolver)) as string[];
console.log(paths) // [ '/pricing', '/about', '/' ]
return {
paths,
fallback: false,
};
}
or to simplify it further
[[...pagePath]].tsx fails when going to localhost:3000/ but does not fail on localhost:3000/about or localhost:3000/pricing.
import { GetStaticPaths, GetStaticProps } from 'next'
interface HomePageProps {
page: string
}
const HomePage = (props:HomePageProps) => {
return <>{props.page}</>
}
export default HomePage
interface HomePageStaticProps {
params: any
previewData:any
}
export async function getStaticProps(props:HomePageStaticProps) {
const params = props.params;
const uid = params?.pagePath?.[params.pagePath.length - 1] || "home";
//const page = await client.getByUID("page", uid);
return {
props: {
page:uid,
},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
const paths = [ '/pricing', '/about', '/' ];
return {
paths,
fallback: false,
};
}
As far as I can see your'e not fetching the right way. In order to to have a clean project I would recommend to use a const var where you can determine between dev and production enviorenment. To do so you can simply create a file for example: constants.js containing the following:
export const baseurl = process.env.NODE_ENV === "production"
? process.env.NEXT_PUBLIC_DOMAIN // <-- your domain on live
: "http://localhost:3000"; // localhost on dev
Now with this you automatically have localhost on your dev. Notice that you need http:// which your'e missing at the moment. Now the next example shows you how to fetch something on your root / by entering the following code:
import { baseurl } from "../utils/constants"; // <-- importing the constant
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch(`${baseurl}/api/posts`)
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
If you are using Create-T3-App
https://github.com/t3-oss/create-t3-app
then
your next.config.mjs will default to this as of Nov 7, 2022
const config = {
reactStrictMode: true,
swcMinify: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
},
};
export default config;
remove
const config = {
reactStrictMode: true,
swcMinify: true,
//i18n: {
// locales: ["en"],
// defaultLocale: "en",
//},
};
export default config;
This will make default "/" pathing work, if you require i18n, I'm sorry I can't help.

Passing variables from middleware to page in Next.js 12 new middleware api

Background to the Question
Vercel recently released their biggest update ever to Next.js. Next.js blog.
They introduced a lot of new features but my favorite is Middleware which:
"enables you to use code over configuration. This gives you full
flexibility in Next.js because you can run code before a request is
completed. Based on the user's incoming request, you can modify the
response by rewriting, redirecting, adding headers, or even streaming
HTML."
The Question
The following structure is used in this question.
- /pages
index.js
signin.js
- /app
_middleware.js # Will run before everything inside /app folder
index.js
The two important files here are /app/_middleware.js and /app/index.js.
// /app/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(req, event) {
const res = { isSignedIn: true, session: { firstName: 'something', lastName: 'else' } }; // This "simulates" a response from an auth provider
if (res.isSignedIn) {
// Continue to /app/index.js
return NextResponse.next();
} else {
// Redirect user
return NextResponse.redirect('/signin');
}
}
// /app/index.js
export default function Home() {
return (
<div>
<h1>Authenticated!</h1>
// session.firstName needs to be passed to this file from middleware
<p>Hello, { session.firstName }</p>
</div>
);
}
In this example /app/index.js needs access to the res.session JSON data. Is it possible to pass it in the NextResponse.next() function or do you need to do something else?
In express you can do res.locals.session = res.session
According to the examples (look specifically at /pages/_middleware.ts and /lib/auth.ts) it looks like the canonical way to do this would be to set your authentication via a cookie.
In your middleware function, that would look like:
// /app/_middleware.js
import { NextResponse } from 'next/server';
export function middleware(req, event) {
const res = { isSignedIn: true, session: { firstName: 'something', lastName: 'else' } }; // This "simulates" a response from an auth provider
if (res.isSignedIn) {
// Continue to /app/index.js
return NextResponse.next().cookie("cookie_key", "cookie_value"); // <--- SET COOKIE
} else {
// Redirect user
return NextResponse.redirect('/signin');
}
}
There's a another way but just like using cookie to achieve this. Just pass you data through headers.
// middleware.ts
async function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('X-HEADER', 'some-value-to-pass');
return response;
}
// _app.ts
function MyApp({ data }) {
// you can access your data here
<div>{data}</div>
}
MyApp.getInitialProps = ({ ctx }) => {
const data = ctx.res.getHeader('X-HEADER');
ctx.res.removeHeader('X-HEADER');
return { data };
};
Only weird solution is to inject your custom object into req.body because next.js v12 middleware doesn't allow altering the NextApiRequest
export const middleware = async (req: NextApiRequest) => {
// return new Response("Hello, world!");
req.body = { ...req.body, foo: "bar" };
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
await middleware(req);
// now req.body.foo=='bar'
}
They do however explain how you can extend middleware here, but the example given (copied below) isn't meaningful enough because it doesnt show how withFoo() is implemented
import { NextApiRequest, NextApiResponse } from 'next'
import { withFoo } from 'external-lib-foo'
type NextApiRequestWithFoo = NextApiRequest & {
foo: (bar: string) => void
}
const handler = (req: NextApiRequestWithFoo, res: NextApiResponse) => {
req.foo('bar') // we can now use `req.foo` without type errors
res.end('ok')
}
export default withFoo(handler)
I assumed based on the above, withFoo.ts should be like this. But still wasn't successful in accessing request.Foo()
import { NextApiHandler, NextApiRequest } from "next";
export const withFoo = (handler: NextApiHandler) => {
//do stuff
};
Maybe someone can chip in?
We found a solution for 12.2+ middleware - published here:
https://clerk.dev/blog/nextjs-pass-value-from-middleware-to-api-routes-and-getserversideprops
And copying here for posterity...
Usage: middleware.js
import { NextResponse } from "next/server";
import { withContext } from "./context";
// Pre-define the possible context keys to prevent spoofing
const allowedContextKeys = ["foo"];
export default withContext(allowedContextKeys, (setContext, req) => {
setContext("foo", "bar");
return NextResponse.next();
});
Usage: API route (Node)
import { getContext } from "../../context";
export default function handler(req, res) {
res.status(200).json({ foo: getContext(req, "foo") });
}
Usage: API route (Edge)
import { getContext } from "../../context";
export default function handler(req) {
return new Response(JSON.stringify({ foo: getContext(req, "foo") }));
}
Usage: getServerSideProps (Edge and Node)
import { getContext } from "../context";
export const getServerSideProps = ({ req }) => {
return { props: { foo: getContext(req, "foo") } };
};
Source: (saved to context.js on your root)
import { NextResponse } from "next/server";
const ctxKey = (key) => `ctx-${key.toLowerCase()}`;
export const getContext = (req, rawKey) => {
const key = ctxKey(rawKey);
let headerValue =
typeof req.headers.get === "function"
? req.headers.get(key) // Edge
: req.headers[key]; // Node;
// Necessary for node in development environment
if (!headerValue) {
headerValue = req.socket?._httpMessage?.getHeader(key);
}
if (headerValue) {
return headerValue;
}
// Use a dummy url because some environments only return
// a path, not the full url
const reqURL = new URL(req.url, "http://dummy.url");
return reqURL.searchParams.get(key);
};
export const withContext = (allowedKeys, middleware) => {
// Normalize allowed keys
for (let i = 0; i < allowedKeys.length; i++) {
if (typeof allowedKeys[i] !== "string") {
throw new Error("All keys must be strings");
}
allowedKeys[i] = ctxKey(allowedKeys[i]);
}
return (req, evt) => {
const reqURL = new URL(req.url);
// First, make sure allowedKeys aren't being spoofed.
// Reliably overriding spoofed keys is a tricky problem and
// different hosts may behave different behavior - it's best
// just to safelist "allowedKeys" and block if they're being
// spoofed
for (const allowedKey of allowedKeys) {
if (req.headers.get(allowedKey) || reqURL.searchParams.get(allowedKey)) {
throw new Error(
`Key ${allowedKey.substring(
4
)} is being spoofed. Blocking this request.`
);
}
}
const data = {};
const setContext = (rawKey, value) => {
const key = ctxKey(rawKey);
if (!allowedKeys.includes(key)) {
throw new Error(
`Key ${rawKey} is not allowed. Add it to withContext's first argument.`
);
}
if (typeof value !== "string") {
throw new Error(
`Value for ${rawKey} must be a string, received ${typeof value}`
);
}
data[key] = value;
};
let res = middleware(setContext, req, evt) || NextResponse.next();
// setContext wasn't called, passthrough
if (Object.keys(data).length === 0) {
return res;
}
// Don't modify redirects
if (res.headers.get("Location")) {
return res;
}
const rewriteURL = new URL(
res.headers.get("x-middleware-rewrite") || req.url
);
// Don't modify cross-origin rewrites
if (reqURL.origin !== rewriteURL.origin) {
return res;
}
// Set context directly on the res object (headers)
// and on the rewrite url (query string)
for (const key in data) {
res.headers.set(key, data[key]);
rewriteURL.searchParams.set(key, data[key]);
}
// set the updated rewrite url
res.headers.set("x-middleware-rewrite", rewriteURL.href);
return res;
};
};

Real time content update with Storyblok not working in production environment Next.js

I am working on a JAMstack website using Next.js (SSG) and Storyblok (Headless CMS) and using the Storyblok Content Delivery API to fetch the content from Storyblok.
I have created the Storyblok service that has Storyblok configurations and Bridge function.
StoryblokService.js
import StoryblokClient from 'storyblok-js-client'
class StoryblokService {
constructor() {
this.devMode = false // Always loads draft
this.token = '#########'
this.client = new StoryblokClient({
accessToken: this.token,
cache: {
clear: 'auto',
type: 'memory'
}
})
this.query = {}
}
getCacheVersion() {
return this.client.cacheVersion
}
get(slug, params) {
params = params || {}
if (this.getQuery('_storyblok') || this.devMode || (typeof window !== 'undefined' && window.storyblok)) {
params.version = 'draft'
}
if (typeof window !== 'undefined' && typeof window.StoryblokCacheVersion !== 'undefined') {
params.cv = window.StoryblokCacheVersion
}
return this.client.get(slug, params)
}
initEditor(reactComponent) {
if (window.storyblok) {
window.storyblok.init()
window.storyblok.on(['change', 'published'], () => location.reload(true))
// this will alter the state and replaces the current story with a current raw story object (no resolved relations or links)
window.storyblok.on('input', (event) => {
if (event.story.content._uid === reactComponent.state.story.content._uid) {
reactComponent.setState({
story: {
...event.story,
content: window.storyblok.addComments(event.story.content, event.story.id)
}
})
}
})
}
}
setQuery(query) {
this.query = query
}
getQuery(param) {
return this.query[param]
}
bridge() {
if (!this.getQuery('_storyblok') && !this.devMode) {
return ''
}
return (<script src={'//app.storyblok.com/f/storyblok-latest.js?t=' + this.token}></script>)
}
}
const storyblokInstance = new StoryblokService()
export default storyblokInstance
I am calling the bridge function and getting the layout content in the layout.js which is getting called from the app.js.
app.js
import React, { useState } from 'react';
import StoryblokService from '../utils/storyblok-service';
function MyApp({ Component, pageProps, layoutContent }) {
return (
<Layout navColor={appendHeaderColor} title={`Test title`} description={`Test description`} content={layoutContent}>
);
}
MyApp.getInitialProps = async (query) => {
StoryblokService.setQuery(query)
const res = await StoryblokService.get('cdn/stories/layout')
const layoutContent = res.data.story.content
return {
layoutContent
}
}
export default MyApp;
Storyblok bridge is getting called like this in layout.js
layout.js
{StoryblokService.bridge()}
Problem Statement
The real-time content update with Storyblok i.e. if I change some content in Storyblok and publish it then as I reload the page the content gets updated in the Next.js Web application also, is working fine in the dev/local environment, but the same thing in the production environment or publishing the Next.js code to Vercel doesn't work.
Seems like the configuration is wrong or maybe it's related to Storyblok webhooks or workflows.
Any help would be appreciated. Thanks in advance!
Did you add the Preview Mode? I had same problem because of missing preview mode.
You should check the get URL whether is correct.

React Native Firebase Notifications aren't always shown

We have a React-Native app on iOS and Android. We use RN 0.61.5 and react-native-firebase 5.5.6.
We use react-native-firebase/notifications to add local notifications on the device. When we test this these notifications are planned correct but we get the feedback from some of our users that they don’t receive the notifications. I have added our notifications.ts file and our NotificationListener.tsx file below.
Does someone have an idea, possible reason of tips for debug/improve/fix our notifications?
Code
notifications.ts:
// Firebase Notifications
// ---
// Custom implementation on top of the Firebase Notifications library.
// This class is responsable for creating, scheduling and re-scheduling notifications.
// ------------------------------------------------------------------------- /
import firebase from "react-native-firebase";
import { Notification } from "react-native-firebase/notifications";
import { Recycle } from "#shd-types/fp-recycle-api";
import { isEmpty } from "ramda";
import { Store } from "../../store";
import { IS_ANDROID, IS_IOS } from "../../utils/device-info";
import i18n, { manualTranslate } from "../../utils/i18n";
import { NotificationsState } from "../../features/notifications/store/notifications.types";
import { CollectionRoundsState } from "../../features/collection-rounds/store/collectionRounds.types";
import { ProfileState } from "../../features/profile/store/profile.types";
const MAX_ALLOWED_NOTIFICATIONS = 64;
class FirebaseNotifications {
private permissionsGranted: boolean = false;
private language: string;
private notificationState: NotificationsState;
private collectionRoundsState: CollectionRoundsState;
private profileState: ProfileState;
constructor() {
// Ask for permission to send notifications
this.init();
const store: any = Store.getState();
this.language = store.global.language;
this.notificationState = store.notifications;
this.collectionRoundsState = store.collectionRounds;
this.profileState = store.profile;
// Create an Android Notification Channel
// More info on this here: https://rnfirebase.io/docs/v5.x.x/notifications/android-channels
this.initChannels();
}
async init(): Promise<void> {
const store: any = Store.getState();
this.language = store.global.language;
this.notificationState = store.notifications;
this.collectionRoundsState = store.collectionRounds;
this.profileState = store.profile;
if (this.permissionsGranted) {
return Promise.resolve();
}
const enabled = await firebase.messaging().hasPermission();
if (!enabled) {
try {
await firebase.messaging().requestPermission();
this.permissionsGranted = true;
} catch (error) {
// User has rejected permissions
this.permissionsGranted = false;
return Promise.reject();
}
} else {
this.permissionsGranted = true;
}
return Promise.resolve();
}
async initChannels() {
// Create the channel groups
await firebase.notifications().android.createChannelGroups([
// Main channel group
new firebase.notifications.Android.ChannelGroup("general", "General"),
// Channel group for each address
new firebase.notifications.Android.ChannelGroup("collections-home", "Home"),
]);
// Build a channel
const channel = new firebase.notifications.Android.Channel("collection-rounds-channel", "Collection Rounds", firebase.notifications.Android.Importance.Max)
.setDescription("Notifications for Collection Rounds")
.setGroup("collections-home");
// Create the channel
firebase.notifications().android.createChannel(channel);
}
// Collection Build Methods
// ------------------------------------------------------------------------- /
setAndroidNotificationProps(notification: Notification, channelId?: string, group?: string) {
// https://rnfirebase.io/docs/v5.x.x/notifications/reference/AndroidNotification
notification
.android.setLocalOnly(true)
.android.setPriority(firebase.notifications.Android.Priority.Max)
// Notification Style
.android.setColor("green") // Sets the accent color to use
.android.setVibrate([500, 500]) // Set notification vibration pattern.
.android.setLights(0xFF97E92A, 500, 500) // Set notification light to green.
.android.setSmallIcon("ic_launcher");
if (channelId) {
notification
.android.setChannelId(channelId);
}
if (group) {
notification
.android.setGroup(group)
.android.setGroupAlertBehaviour(firebase.notifications.Android.GroupAlert.All);
}
}
setIOSNotificationProps(notification: Notification, category?: string) {
// https://rnfirebase.io/docs/v5.x.x/notifications/reference/AndroidNotification
notification
.ios.setHasAction(false); // IOS 9 only.
if (category) {
notification
.ios.setCategory(category);
}
}
buildOpenAppNotification(fireDate: number): Notification {
const notification = new firebase.notifications.Notification()
.setNotificationId(`general-open-app-reminder-${fireDate}`)
.setTitle(i18n._("notifications.appRefresh.title"))
.setBody(i18n._("notifications.appRefresh.body"))
.setSound("default")
.setData({
schedule: {
fireDate,
},
});
if (IS_ANDROID) {
this.setAndroidNotificationProps(notification, "general");
}
if (IS_IOS) {
this.setIOSNotificationProps(notification, "general");
notification
.ios.setBadge(1);
}
return notification;
}
buildCollectionNotification(collection: Recycle.Collection.CollectionRound, fireDate?: number, sameDay = false): Notification {
const collectionTypeNames = collection.types.map((collectionType: Recycle.Type.Item) => manualTranslate(collectionType.name, this.language));
const notificationTitle = sameDay
? `${i18n._("notifications.collectionSameDay.title")}: ${collectionTypeNames.join(", ")}`
: `${i18n._("notifications.collectionDayBefore.title")}: ${collectionTypeNames.join(", ")}`;
// TODO: Map this to the location, if multiple
const notificationSubtitle = i18n._(this.profileState.address[0].alias);
const notificationBody = sameDay
? i18n._("notifications.collectionSameDay.body")
: i18n._("notifications.collectionDayBefore.body");
const notificationBodyExtended = sameDay
? i18n._("notifications.collectionSameDay.bodyExtended")
: i18n._("notifications.collectionDayBefore.bodyExtended");
const notification = new firebase.notifications.Notification()
.setNotificationId(`notification-collection-${collection.id}-${fireDate}`)
.setTitle(notificationTitle)
.setSubtitle(notificationSubtitle)
.setBody(notificationBody)
.setData({
...collection,
schedule: {
fireDate,
},
})
.setSound("default");
if (IS_ANDROID) {
this.setAndroidNotificationProps(notification, "collection-rounds-channel", "collections");
notification.android
.setBigText(
notificationBodyExtended,
notificationTitle,
notificationBody
);
}
if (IS_IOS) {
this.setIOSNotificationProps(notification, "collections");
}
return notification;
}
resetNotificationBadge() {
if (IS_IOS) {
const notifications = firebase.notifications();
notifications.setBadge(0);
}
}
// Scheduling Methods
// ------------------------------------------------------------------------- /
async rescheduleNotifications(collections?: Recycle.Collection.CollectionRound[]) {
// Clear all scheduled notifications
const notifications = await firebase.notifications().getScheduledNotifications();
if (notifications) {
notifications
.forEach((notification: Notification) => {
firebase.notifications().cancelNotification(notification.notificationId);
});
}
collections = collections ? collections : this.collectionRoundsState.data;
if (!collections) {
return;
}
// Check if the user has given permission to receive notifications
try {
await this.init();
} catch (err) {
return;
}
// Fetch the notificationsSettings
const homeNotificationSettings = this.notificationState.settings.home;
// TODO
// 1. Account for the amount of notifications (e.g. the day before AND the current day)
// 2. Account for the amount of addresses to schedule for.
//
// We will simulate this for now. (simplified)
const numberOfNotificationsPerCollection = Object.values(homeNotificationSettings.time).filter((timeSetting) => timeSetting.enabled).length;
const numberOfAddresses = 1;
const numberOfCollectionsToSchedule = Math.floor(MAX_ALLOWED_NOTIFICATIONS / (numberOfAddresses * numberOfNotificationsPerCollection)) - 1;
// TODO
// 1. Account for the time when we want the notification.
let lastNotificationTime = 0;
let collectionTypesToNotNotify: string[] = [];
// Filter out the collectionTypes we don't want a notification for.
if (homeNotificationSettings) {
collectionTypesToNotNotify = Object.values(homeNotificationSettings.categories)
.filter((category) => !category.enabled )
.map((category) => category.key);
}
// Reschedule all collections
collections
.filter((collection) => {
// Filter out collections that have passed
if (new Date(collection.date).valueOf() < new Date().valueOf()) {
return false;
}
// Filter out collections that are not wanted
if (collectionTypesToNotNotify.length) {
const allow = [];
const localCollectionTypes = collection.types.map((type) => type.id);
for (const collectionType of localCollectionTypes) {
allow.push(!collectionTypesToNotNotify.includes(collectionType));
}
// The collection has at least 1 item we want notifications for.
return allow.includes(true);
}
return true;
})
.map((collection) => {
// Filter out the collections the user does not want.
if (!collectionTypesToNotNotify.length) {
return collection;
}
const localCollectionTypes = collection.types.map((type) => type.id);
return {
...collection,
types: localCollectionTypes.filter((collectionType) => {
return !collectionTypesToNotNotify.includes(collectionType);
}),
};
})
.slice(0, numberOfCollectionsToSchedule)
.forEach((collection, index, arr) => {
const collectionDay = new Date(collection.date);
let collectionNotificationTime = 0;
let collectionHour = 0;
let collectionMinutes = 0;
if (homeNotificationSettings.time.dayBefore.enabled) {
// Get the day before
const collectionNotificationDate = new Date(collectionDay).setDate(collectionDay.getDate() - 1);
// Set the notification hour
collectionHour = homeNotificationSettings.time.dayBefore.hours;
collectionMinutes = homeNotificationSettings.time.dayBefore.minutes;
collectionNotificationTime = new Date(collectionNotificationDate).setHours(collectionHour, collectionMinutes);
// Schedule the notification if it triggers in the future
this.scheduleCollectionNotification(collection, collectionNotificationTime);
}
if (homeNotificationSettings.time.dayOf.enabled) {
// Set the notification hour
collectionHour = homeNotificationSettings.time.dayOf.hours;
collectionMinutes = homeNotificationSettings.time.dayOf.minutes;
collectionNotificationTime = new Date(collectionDay).setHours(collectionHour, collectionMinutes);
// Schedule the notification if it triggers in the future
this.scheduleCollectionNotification(collection, collectionNotificationTime, true);
}
// Schedule the "open your app please" notification after 10 notifcations
// And after the last one
if ((index !== 0 && index % 10 === 0) || (index === arr.length - 1)) {
// Plan the reminder 1 day after the previous notification
const collectionDate = new Date(collectionDay).setDate(collectionDay.getDate() + 1);
lastNotificationTime = new Date(collectionDate).setHours(collectionHour, collectionMinutes);
firebase.notifications().scheduleNotification(
this.buildOpenAppNotification(lastNotificationTime),
{ fireDate: lastNotificationTime }
);
}
});
}
scheduleCollectionNotification(collection: Recycle.Collection.CollectionRound, fireDate: number, sameDay = false) {
// Schedule the notification if it triggers in the future
if (fireDate > new Date().valueOf() && !isEmpty(this.notificationState.settings.home.categories)) {
firebase.notifications().scheduleNotification(
this.buildCollectionNotification(collection, fireDate, sameDay),
{ fireDate }
);
}
}
}
export default new FirebaseNotifications();
NotificationListener.tsx:
import React from "react";
import firebase from "react-native-firebase";
import { Notification, NotificationOpen } from "react-native-firebase/notifications";
interface Props {
children: any;
}
class NotificationListener extends React.Component<Props> {
private notificationDisplayedListener: (notification?: Notification) => void;
private notificationListener: (notification?: Notification) => void;
private notificationOpenedListener: (notificationOpen?: NotificationOpen) => void;
constructor(props: Props) {
super(props);
this.notificationDisplayedListener = firebase.notifications().onNotificationDisplayed(this.onNotificationDisplayed);
this.notificationListener = firebase.notifications().onNotification(this.onNotification);
this.notificationOpenedListener = firebase.notifications().onNotificationOpened(this.onNotificationOpened);
this.onNotificationDisplayed = this.onNotificationDisplayed.bind(this);
this.onNotification = this.onNotification.bind(this);
this.onNotificationOpened = this.onNotificationOpened.bind(this);
}
// Lifecycle Methods
// ------------------------------------------------------------------------- /
// https://rnfirebase.io/docs/v5.x.x/notifications/receiving-notifications#App-Closed
async componentDidMount() {
const notificationOpen: NotificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
// App was opened by a notification
this.onNotificationOpened(notificationOpen);
}
}
componentWillUnmount() {
this.notificationDisplayedListener();
this.notificationListener();
this.notificationOpenedListener();
}
// Callback Methods
// ------------------------------------------------------------------------- /
// Triggered when a particular notification has been displayed
onNotificationDisplayed(notification: Notification) {
// ANDROID: Remote notifications do not contain the channel ID. You will have to specify this manually if you'd like to re-display the notification.
// Leave this for debugging purposes
// console.log("Notification Displayed!:", notification);
}
// Triggered when a particular notification has been received, but not displayed
onNotification(notification: Notification) {
// Leave this for debugging purposes
// console.log("Notification received, but not displayed!:", notification);
// Force the notification in the OS NotificationBar
firebase.notifications().displayNotification(notification);
}
// Triggered when a particular notification has been clicked / tapped / opened whent he app is in the foreground or background.
onNotificationOpened(notificationOpen: NotificationOpen) {
// Get the action triggered by the notification being opened
const action = notificationOpen.action;
// Get information about the notification that was opened
const notification: Notification = notificationOpen.notification;
// Leave this for debugging purposes
// console.log("Notification Opened!:", notificationOpen);
// Remove the notification once it has been openend.
firebase.notifications().removeDeliveredNotification(notificationOpen.notification.notificationId);
}
// Render Methods
// ------------------------------------------------------------------------- /
render() {
return this.props.children;
}
}
export default NotificationListener;```

REST API auth error : WooCommerce

Been trying to fetch products from WooCommerce REST API now forever and my brain is bleeding :'( I followed all instructions on woocommerce and github/woocommerce and I can't for my life get anything in Postman with Basic Auth:
But when I select Auth 1.0 - I get all products:
But then if I take the Auth 1.0 generated URL and put it in my browser:
..Instructions under Authentication over HTTP (here) describes the parameters which are generated in URL automatically when i select Auth 1.0 in Postman - but how am I gonna generate those in my React component?
const APP_URL = 'http://0.0.0.0:80'
const CONSUMER_KEY = 'ck_xxxx'
const CONSUMER_SECRET = 'cs_xxxx'
const PRODUCT_URL = `${APP_URL}/wp-json/wc/v2/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}`
fetch(PRODUCT_URL).then(res => {
if(res.status === 200){
return json
} else {
console.log("ERROR RESPONSE STATUS: " + status);
}
}).then( json => {
console.log(json)
})
})
So thankful for all input!
I think this problem may be solved by below code using "Interceptor" concept...
import {
Injectable,
// Injector
} from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '#angular/common/http';
// import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
// import { AuthService } from './auth.service';
import { config } from '../config/config';
#Injectable()
export class AppInterceptor implements HttpInterceptor {
constructor(
// private injector: Injector,
// private router: Router
) { }
private includeWooAuth(url) {
const wooAuth = `consumer_key=${config.consumerKey}&consumer_secret=${config.secretKey}`;
const hasQuery = url.includes('?');
let return_url = '';
if (hasQuery) {
return_url = wooAuth;
} else {
return_url = '?' + wooAuth;
}
return return_url;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const auth = this.injector.get(AuthService);
const authRequest = request.clone({
setHeaders: {
// Authorization: `Bearer ${auth.getToken()}`
},
url: `${config.basePath}/${request.url}${this.includeWooAuth(request.url)}`
});
return next.handle(authRequest)
.catch(err => {
if (err instanceof HttpErrorResponse && err.status === 0) {
console.log('Check Your Internet Connection And Try again Later');
} else if (err instanceof HttpErrorResponse && err.status === 401) {
// auth.setToken(null);
// this.router.navigate(['/', 'login']);
}
return Observable.throw(err);
});
}
}
This code will be kept into http.interceptor.ts. Obviously, you should initialize the consumer key and other details of woocommerce API into a constant variable. After that you create a service for show the list of the product like this:
retriveProducts(query: ProductQuery = {}): Observable<RetriveProductsResponse> {
return this.httpClient.get(`products`, {params: this.wooHelper.includeQuery(query), observe: 'response'})
.pipe(
map(value => this.wooHelper.includeResponseHeader(value)),
catchError(err => this.wooHelper.handleError(err)));
}
And call this service to the product.ts file like this:
getProducts() {
this.woocommerceProductsService.retriveProducts()
.subscribe(productResponse => {
this.products = productResponse.products;
}, error => this.errorMessage = <any>error);
}
I have used these above code into my project. I think it will help you.

Resources