From 8f553fdc3329468faa5baa79492b6d8b5eadb7cb Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 15:24:40 +0300 Subject: [PATCH 1/6] feat: add change browser modal --- src/Modals.tsx | 21 +++++--- .../ChangeBrowserModal.scss | 54 +++++++++++++++++++ .../utils/ChangeBrowserModal/index.tsx | 34 ++++++++++++ src/utils/pwa.ts | 9 ++++ 4 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/components/utils/ChangeBrowserModal/ChangeBrowserModal.scss create mode 100644 src/components/utils/ChangeBrowserModal/index.tsx diff --git a/src/Modals.tsx b/src/Modals.tsx index 3d8b0403..aa78c55f 100644 --- a/src/Modals.tsx +++ b/src/Modals.tsx @@ -5,6 +5,7 @@ import { AnimatePresence } from 'framer-motion' import { PreferencesModal } from '@/components/notifications/NotificationsLayout/PreferencesModal' import { UnsubscribeModal } from '@/components/notifications/NotificationsLayout/UnsubscribeModal' +import ChangeBrowserModal from '@/components/utils/ChangeBrowserModal' import NotificationPwaModal from '@/components/utils/NotificationPwaModal' import PwaModal from '@/components/utils/PwaModal' import W3iContext from '@/contexts/W3iContext/context' @@ -15,7 +16,7 @@ import { checkIfNotificationModalClosed, notificationsEnabledInBrowser } from '@/utils/notifications' -import { isMobileButNotInstalledOnHomeScreen } from '@/utils/pwa' +import { isChrome, isIOS, isMobileButNotInstalledOnHomeScreen } from '@/utils/pwa' import { notificationPwaModalService, signatureModalService } from '@/utils/store' import { isMobile } from '@/utils/ui' @@ -34,6 +35,11 @@ export const Modals = () => { const notificationModalClosed = checkIfNotificationModalClosed() const explicitlyDeniedOnDesktop = !isMobile() && window.Notification?.permission === 'denied' + const shouldShowChangeBrowserModal = isIOS() && isChrome() + const shouldShowPWAModal = isMobileButNotInstalledOnHomeScreen() && !shouldShowChangeBrowserModal + const shouldShowSignatureModal = isSignatureModalOpen && !shouldShowChangeBrowserModal + const shouldShowUnsubscribeModalOpen = isUnsubscribeModalOpen && !shouldShowChangeBrowserModal + const shouldShowPreferencesModalOpen = isPreferencesModalOpen && !shouldShowChangeBrowserModal const shouldShowNotificationModal = notificationsEnabledInBrowser() && @@ -42,7 +48,8 @@ export const Modals = () => { !notificationsEnabled && Boolean(notifyRegisteredKey) && !isSignatureModalOpen && - !notificationModalClosed + !notificationModalClosed && + !shouldShowChangeBrowserModal useEffect(() => { const notifySignatureRequired = Boolean(notifyRegisterMessage) && !notifyRegisteredKey @@ -67,17 +74,19 @@ export const Modals = () => { return ( - {isUnsubscribeModalOpen && } + {shouldShowUnsubscribeModalOpen && } - {isPreferencesModalOpen && } + {shouldShowPreferencesModalOpen && } - {isSignatureModalOpen && ( + {shouldShowSignatureModal && ( )} - {isMobileButNotInstalledOnHomeScreen() && } + {shouldShowPWAModal && } {isNotificationPwaModalOpen && } + + {shouldShowChangeBrowserModal && } ) } diff --git a/src/components/utils/ChangeBrowserModal/ChangeBrowserModal.scss b/src/components/utils/ChangeBrowserModal/ChangeBrowserModal.scss new file mode 100644 index 00000000..ee627eb7 --- /dev/null +++ b/src/components/utils/ChangeBrowserModal/ChangeBrowserModal.scss @@ -0,0 +1,54 @@ +.ChangeBrowserModal { + padding: 3em; + display: flex; + flex-direction: column; + text-align: center; + align-items: center; + gap: 2em; + position: relative; + + &__close-button { + svg { + color: var(--fg-color-3); + } + } + + &__header { + display: flex; + justify-content: flex-end; + width: 100%; + align-items: center; + } + + &__background { + position: absolute; + z-index: -999; + top: -50%; + left: -50%; + } + + &__icon { + display: block; + width: 3em; + background: white; + width: 4em; + height: 4em; + display: grid; + place-items: center; + padding: 0.75em; + border-radius: 13px; + box-shadow: 0px 8px 32px 17.5px hsla(0, 0%, 0%, 0.12); + } + + &__warning { + color: var(--error-color-1); + } + + &__content { + display: flex; + flex-direction: column; + gap: 1em; + align-items: center; + text-align: center; + } +} diff --git a/src/components/utils/ChangeBrowserModal/index.tsx b/src/components/utils/ChangeBrowserModal/index.tsx new file mode 100644 index 00000000..07e4ea7a --- /dev/null +++ b/src/components/utils/ChangeBrowserModal/index.tsx @@ -0,0 +1,34 @@ +import React from 'react' + +import BackgroundImage from '@/assets/IntroBackground.png' +import { Modal } from '@/components/general/Modal/Modal' +import Text from '@/components/general/Text' +import { pwaModalService } from '@/utils/store' + +import './ChangeBrowserModal.scss' + +export const ChangeBrowserModal: React.FC = () => { + return ( + +
+
+ +
+
+ Web3Inbox icon +
+ Change Browser +
+ + To install the app, you need to add this to your home screen. + + + Please open app.web3inbox.com in Safari and follow the instructions. + +
+
+
+ ) +} + +export default ChangeBrowserModal diff --git a/src/utils/pwa.ts b/src/utils/pwa.ts index 1d2c8e7f..468b9125 100644 --- a/src/utils/pwa.ts +++ b/src/utils/pwa.ts @@ -1,3 +1,7 @@ +import { detect } from 'detect-browser' + +const browser = detect() + export const isInstalledOnHomescreen = () => { // on Android and iOS, display mode is set to // standalone when the app is opened from home screen @@ -8,5 +12,10 @@ export const isInstalledOnHomescreen = () => { export const isMobileBrowser = () => /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) +export const isIOS = () => /iPhone|iPad|iPod/i.test(navigator.userAgent) + +export const isChrome = () => + browser?.name ? /chrome|crios|edge-chromium/i.test(browser?.name) : false + export const isMobileButNotInstalledOnHomeScreen = () => isMobileBrowser() && !isInstalledOnHomescreen() From d541c91230fcfeedff799a3c050a7016e2ff221d Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 20:21:32 +0300 Subject: [PATCH 2/6] refactor: update device and browser check logics --- src/Modals.tsx | 8 ++++---- src/utils/pwa.ts | 29 +++++++++++++---------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Modals.tsx b/src/Modals.tsx index aa78c55f..323c916b 100644 --- a/src/Modals.tsx +++ b/src/Modals.tsx @@ -16,7 +16,7 @@ import { checkIfNotificationModalClosed, notificationsEnabledInBrowser } from '@/utils/notifications' -import { isChrome, isIOS, isMobileButNotInstalledOnHomeScreen } from '@/utils/pwa' +import { isAppleMobile, isMobileButNotInstalledOnHomeScreen, isSafari } from '@/utils/pwa' import { notificationPwaModalService, signatureModalService } from '@/utils/store' import { isMobile } from '@/utils/ui' @@ -35,8 +35,8 @@ export const Modals = () => { const notificationModalClosed = checkIfNotificationModalClosed() const explicitlyDeniedOnDesktop = !isMobile() && window.Notification?.permission === 'denied' - const shouldShowChangeBrowserModal = isIOS() && isChrome() - const shouldShowPWAModal = isMobileButNotInstalledOnHomeScreen() && !shouldShowChangeBrowserModal + const shouldShowChangeBrowserModal = isAppleMobile ? !isSafari : false + const shouldShowPWAModal = isMobileButNotInstalledOnHomeScreen && !shouldShowChangeBrowserModal const shouldShowSignatureModal = isSignatureModalOpen && !shouldShowChangeBrowserModal const shouldShowUnsubscribeModalOpen = isUnsubscribeModalOpen && !shouldShowChangeBrowserModal const shouldShowPreferencesModalOpen = isPreferencesModalOpen && !shouldShowChangeBrowserModal @@ -44,7 +44,7 @@ export const Modals = () => { const shouldShowNotificationModal = notificationsEnabledInBrowser() && !explicitlyDeniedOnDesktop && - !isMobileButNotInstalledOnHomeScreen() && + !isMobileButNotInstalledOnHomeScreen && !notificationsEnabled && Boolean(notifyRegisteredKey) && !isSignatureModalOpen && diff --git a/src/utils/pwa.ts b/src/utils/pwa.ts index 468b9125..161ac6b5 100644 --- a/src/utils/pwa.ts +++ b/src/utils/pwa.ts @@ -1,21 +1,18 @@ -import { detect } from 'detect-browser' +export const isInstalledOnHomeScreen = window.matchMedia('(display-mode: standalone)').matches -const browser = detect() +export const isMobileBrowser = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) -export const isInstalledOnHomescreen = () => { - // on Android and iOS, display mode is set to - // standalone when the app is opened from home screen - const displayIsStandalone = window.matchMedia('(display-mode: standalone)').matches +export const isAppleMobile = /iPhone|iPad|iPod/i.test(navigator.userAgent) - return displayIsStandalone -} +/** + * vendor is deprecated but it's the only way to detect if the browser is belongs to Apple product (Safari) + * There are variety of browsers that are based on Chromium (Chrome, Arc, Brave, etc.) but `navigator` object doesn't provide a unique way to detect them. + * `userAgent` or other fields doesn't give us ability to check if it's Safari or not. + * So, we need to check the vendor to make sure that the browser is Safari to make conditional rendering. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor + */ +const isAppleVendor = /Apple Computer, Inc./i.test(navigator.vendor) -export const isMobileBrowser = () => /iPhone|iPad|iPod|Android/i.test(navigator.userAgent) +export const isSafari = isAppleVendor && navigator.userAgent.match(/Safari/i) -export const isIOS = () => /iPhone|iPad|iPod/i.test(navigator.userAgent) - -export const isChrome = () => - browser?.name ? /chrome|crios|edge-chromium/i.test(browser?.name) : false - -export const isMobileButNotInstalledOnHomeScreen = () => - isMobileBrowser() && !isInstalledOnHomescreen() +export const isMobileButNotInstalledOnHomeScreen = isMobileBrowser && !isInstalledOnHomeScreen From 6661ce8bd4ae601c078794f6ba9f750a32dad684 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 21:06:29 +0300 Subject: [PATCH 3/6] refactor: update safari check logics --- src/Modals.tsx | 6 +++--- src/components/utils/PwaModal/index.tsx | 18 +++++++++++++++++- src/utils/pwa.ts | 15 +++++++-------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Modals.tsx b/src/Modals.tsx index 323c916b..ea91f84b 100644 --- a/src/Modals.tsx +++ b/src/Modals.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useMemo } from 'react' +import { useContext, useEffect } from 'react' import { useWeb3Modal } from '@web3modal/wagmi/react' import { AnimatePresence } from 'framer-motion' @@ -16,7 +16,7 @@ import { checkIfNotificationModalClosed, notificationsEnabledInBrowser } from '@/utils/notifications' -import { isAppleMobile, isMobileButNotInstalledOnHomeScreen, isSafari } from '@/utils/pwa' +import { isAppleMobile, isMobileButNotInstalledOnHomeScreen, isNonSafari } from '@/utils/pwa' import { notificationPwaModalService, signatureModalService } from '@/utils/store' import { isMobile } from '@/utils/ui' @@ -35,7 +35,7 @@ export const Modals = () => { const notificationModalClosed = checkIfNotificationModalClosed() const explicitlyDeniedOnDesktop = !isMobile() && window.Notification?.permission === 'denied' - const shouldShowChangeBrowserModal = isAppleMobile ? !isSafari : false + const shouldShowChangeBrowserModal = isAppleMobile ? isNonSafari : false const shouldShowPWAModal = isMobileButNotInstalledOnHomeScreen && !shouldShowChangeBrowserModal const shouldShowSignatureModal = isSignatureModalOpen && !shouldShowChangeBrowserModal const shouldShowUnsubscribeModalOpen = isUnsubscribeModalOpen && !shouldShowChangeBrowserModal diff --git a/src/components/utils/PwaModal/index.tsx b/src/components/utils/PwaModal/index.tsx index 7c7b02b6..39a89e78 100644 --- a/src/components/utils/PwaModal/index.tsx +++ b/src/components/utils/PwaModal/index.tsx @@ -4,11 +4,13 @@ import { detect } from 'detect-browser' import { Link } from 'react-router-dom' import BackgroundImage from '@/assets/IntroBackground.png' +import Button from '@/components/general/Button' import AndroidShareIcon from '@/components/general/Icon/AndroidShare' import IShareIcon from '@/components/general/Icon/IShare' import { Modal } from '@/components/general/Modal/Modal' import Text from '@/components/general/Text' import { web3InboxURLs } from '@/constants/navigation' +import { isAppleMobile } from '@/utils/pwa' import { pwaModalService } from '@/utils/store' import './PwaModal.scss' @@ -58,7 +60,14 @@ export const PwaModal: React.FC = () => { Web3Inbox icon
- Install Web3Inbox + Install PWA +
@@ -71,6 +80,13 @@ export const PwaModal: React.FC = () => { {getMobilePlatformIcon()} and “{getPlatformInstallText()}”
+ {isAppleMobile ? ( +
+ + Cannot see the option? Make sure you're using Safari. + +
+ ) : null}
Learn more at  diff --git a/src/utils/pwa.ts b/src/utils/pwa.ts index 161ac6b5..35fa83ce 100644 --- a/src/utils/pwa.ts +++ b/src/utils/pwa.ts @@ -4,15 +4,14 @@ export const isMobileBrowser = /iPhone|iPad|iPod|Android/i.test(navigator.userAg export const isAppleMobile = /iPhone|iPad|iPod/i.test(navigator.userAgent) +export const isNonSafari = /CriOS|FxiOS/i.test(navigator.userAgent) + /** - * vendor is deprecated but it's the only way to detect if the browser is belongs to Apple product (Safari) - * There are variety of browsers that are based on Chromium (Chrome, Arc, Brave, etc.) but `navigator` object doesn't provide a unique way to detect them. - * `userAgent` or other fields doesn't give us ability to check if it's Safari or not. - * So, we need to check the vendor to make sure that the browser is Safari to make conditional rendering. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor + * It's not possible to detect Safari on iOS. `navigator` object doesn't have enough information to detect if the running browser is exactly Safari. + * There are several fields like `vendor` (deprecated), `userAgent`, `userAgentData`. But these values can mislead us since they can return the same values for different browsers. + * Instead of detecting Safari and doing conditional renderings with this value, prefer different solution. + * Tested mobile browsers: Safari, Chrome, Firefox, Arc, Brave */ -const isAppleVendor = /Apple Computer, Inc./i.test(navigator.vendor) - -export const isSafari = isAppleVendor && navigator.userAgent.match(/Safari/i) +export const isSafari = isAppleMobile && navigator.userAgent.match(/Safari/i) export const isMobileButNotInstalledOnHomeScreen = isMobileBrowser && !isInstalledOnHomeScreen From 8474bf20dcd1b427fbfd193e224f3ab882f0e8e8 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 21:12:17 +0300 Subject: [PATCH 4/6] chore: update pwa variables and comments --- src/utils/pwa.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/utils/pwa.ts b/src/utils/pwa.ts index 35fa83ce..cd7abfdb 100644 --- a/src/utils/pwa.ts +++ b/src/utils/pwa.ts @@ -4,14 +4,12 @@ export const isMobileBrowser = /iPhone|iPad|iPod|Android/i.test(navigator.userAg export const isAppleMobile = /iPhone|iPad|iPod/i.test(navigator.userAgent) -export const isNonSafari = /CriOS|FxiOS/i.test(navigator.userAgent) - /** - * It's not possible to detect Safari on iOS. `navigator` object doesn't have enough information to detect if the running browser is exactly Safari. - * There are several fields like `vendor` (deprecated), `userAgent`, `userAgentData`. But these values can mislead us since they can return the same values for different browsers. - * Instead of detecting Safari and doing conditional renderings with this value, prefer different solution. - * Tested mobile browsers: Safari, Chrome, Firefox, Arc, Brave + * WARN: Browser check can be tricky and not reliable at some cases. + * Some non-Safari browsers can return the same `userAgent` value as Safari, so it might not be quite possible to detect if the browser is Safari or not. + * There are several fields like `vendor` (deprecated), `userAgent`, `userAgentData`. Some of these are deprecated and some are returning similar values across different browsers like `userAgent`. + * So be aware that is not perfect solution. */ -export const isSafari = isAppleMobile && navigator.userAgent.match(/Safari/i) +export const isNonSafari = /CriOS|FxiOS/i.test(navigator.userAgent) export const isMobileButNotInstalledOnHomeScreen = isMobileBrowser && !isInstalledOnHomeScreen From 20c633b600d3567ff7a3756c350a76f60f114340 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 21:13:29 +0300 Subject: [PATCH 5/6] chore: remove copy button --- src/components/utils/PwaModal/index.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/utils/PwaModal/index.tsx b/src/components/utils/PwaModal/index.tsx index 39a89e78..c3c8aa2d 100644 --- a/src/components/utils/PwaModal/index.tsx +++ b/src/components/utils/PwaModal/index.tsx @@ -61,13 +61,6 @@ export const PwaModal: React.FC = () => {
Install PWA -
From 3bb2caf33a10a8f9ed878bfbe43258959f670569 Mon Sep 17 00:00:00 2001 From: enesozturk Date: Mon, 15 Jan 2024 21:13:54 +0300 Subject: [PATCH 6/6] chore: remove safari warning --- src/components/utils/PwaModal/index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/utils/PwaModal/index.tsx b/src/components/utils/PwaModal/index.tsx index c3c8aa2d..bfbb4f42 100644 --- a/src/components/utils/PwaModal/index.tsx +++ b/src/components/utils/PwaModal/index.tsx @@ -4,13 +4,11 @@ import { detect } from 'detect-browser' import { Link } from 'react-router-dom' import BackgroundImage from '@/assets/IntroBackground.png' -import Button from '@/components/general/Button' import AndroidShareIcon from '@/components/general/Icon/AndroidShare' import IShareIcon from '@/components/general/Icon/IShare' import { Modal } from '@/components/general/Modal/Modal' import Text from '@/components/general/Text' import { web3InboxURLs } from '@/constants/navigation' -import { isAppleMobile } from '@/utils/pwa' import { pwaModalService } from '@/utils/store' import './PwaModal.scss' @@ -73,13 +71,6 @@ export const PwaModal: React.FC = () => { {getMobilePlatformIcon()} and “{getPlatformInstallText()}”
- {isAppleMobile ? ( -
- - Cannot see the option? Make sure you're using Safari. - -
- ) : null}
Learn more at