Skip to content

Instantly share code, notes, and snippets.

@savayer
Created November 8, 2023 08:04
Show Gist options
  • Save savayer/3748169b9d43f6b384a5db6978cc1d64 to your computer and use it in GitHub Desktop.
Save savayer/3748169b9d43f6b384a5db6978cc1d64 to your computer and use it in GitHub Desktop.
import Cookies from 'js-cookie';
import React, { useEffect, useState } from 'react';
import { Close } from './icons';
import { classNames, getOs } from './utils';
/**
* Lookup key for the Cookie value to hide the app banner for a period of time.
**/
const APP_BANNER_IS_HIDDEN = 'app_banner_is_hidden';
/**
* Function that sets the app banner as hidden and also sets the app banner's hidden
* TTL cookie.
* @callback HideAppBannerFunction
* @returns {undefined}
*
* @typedef UseAppUrlOptions
* @property {string} playStoreUrl Google Play Store URL.
* @property {string} appStoreUrl IOS App Store URL.
*
* @typedef BaseAppBannerProps
* @property {string} title Title of the app banner.
* @property {string} description Description of the app banner.
* @property {string} linkText The text for the app banner's view/install app link element.
* @property {React.ReactElement} logo The application's logo.
*
* @typedef {BaseAppBannerProps & UseAppUrlOptions} AppBannerProps
**/
/**
* Hook for managing the app banner's hiding logic.
*
* @param {number | Date} [hiddenTtl=90] The amount of time that the app banner should be hidden
* for before prompting the user again once the user decides to hide it. Could be either a date or a
* number value for the number of days. Default is 90 days (approx. 3 months).
*
* @returns {[boolean, HideAppBannerFunction]} A tuple: first item is the app banner's hidden
* state, the second item is a function to set the app banner as hidden.
**/
function useHideAppBanner(hiddenTtl = 90) {
const [isHidden, setIsHidden] = useState(true);
useEffect(() => {
if (Cookies.get(APP_BANNER_IS_HIDDEN) === undefined) {
setIsHidden(false);
}
}, []);
const hide = () => {
setIsHidden(true);
Cookies.set(APP_BANNER_IS_HIDDEN, true, { expires: hiddenTtl });
};
return [isHidden, hide];
}
/**
* Hook for retrieving the mobile app URL based on the user agent.
*
* @param options {UseAppUrlOptions}
*
* @returns {string | null} The application URL if the user agent is either on an Android or IOS
* operating system, `null` otherwise.
**/
function useAppUrl({ playStoreUrl, appStoreUrl }) {
const [appUrl, setAppUrl] = useState(null);
useEffect(() => {
const os = getOs(window.navigator);
switch (os) {
case 'android':
setAppUrl(playStoreUrl);
break;
case 'ios':
setAppUrl(appStoreUrl);
break;
}
}, [playStoreUrl, appStoreUrl]);
return appUrl;
}
/**
* @param {AppBannerProps} props
**/
export function AppBanner({
title,
description,
logo,
playStoreUrl,
appStoreUrl,
linkText,
}) {
const [isAppBannerHidden, hideAppBanner] = useHideAppBanner();
const appUrl = useAppUrl({ playStoreUrl, appStoreUrl });
return (
<div
className={classNames(
'flex items-center justify-between bg-gray-100 p-4',
isAppBannerHidden && '!hidden',
)}
>
<div className="flex items-center">
<Close
className="h-3 shrink-0 cursor-pointer fill-gray-600"
onClick={hideAppBanner}
/>
<div className="mx-3 h-14 w-14 shrink-0 rounded-lg border border-gray-200 bg-white p-2">
{logo}
</div>
<div className="flex-1">
<h1 className="w-full text-sm font-medium text-gray-600">{title}</h1>
<p className="w-full text-xs text-gray-400">{description}</p>
</div>
</div>
<a
href={appUrl}
target="_blank"
rel="noreferrer noopenner"
className="ml-4 cursor-pointer font-semibold text-blue-500"
>
{linkText}
</a>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment