-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
180 additions
and
230 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,12 @@ | ||
import {Provider} from "@onflow/typedefs" | ||
|
||
export type AppInfo = { | ||
name?: string | ||
icon?: string | ||
} | ||
|
||
export type NotificationInfo = { | ||
walletProvider?: Provider | ||
} | ||
|
||
export type ActiveNotificationInfo = NotificationInfo & {id: number} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import {useState, useRef} from "preact/hooks" | ||
import {useBreakpoint} from "../hooks/useBreakpoint" | ||
import {Breakpoint} from "../constants" | ||
import {isMobile as _isMobile, openDeeplink} from "../../utils" | ||
|
||
import mobileIcon from "../assets/mobile.png" | ||
import {ActiveNotificationInfo} from "../../types/types" | ||
|
||
export function Notification({walletProvider}: ActiveNotificationInfo) { | ||
const onOpenApp = () => { | ||
openDeeplink((walletProvider as any)?.uid) | ||
} | ||
|
||
const isDesktop = useBreakpoint(Breakpoint.MD) | ||
const isMobile = _isMobile() | ||
const [isVisible, setIsVisible] = useState(true) | ||
const timer = useRef<NodeJS.Timeout>() | ||
|
||
if (!isVisible) return null | ||
|
||
return ( | ||
<div | ||
className="animate-slideUp fixed bottom-3 left-3 right-3 z-[2147483647] flex max-w-sm | ||
overflow-hidden rounded-lg border border-gray-200 bg-white p-4 shadow-lg | ||
md:bottom-4 md:left-auto md:right-4 dark:bg-gray-800 dark:border-gray-700" | ||
role="alert" | ||
onClick={e => { | ||
if (!isDesktop) { | ||
e.stopPropagation() | ||
onOpenApp?.() | ||
} | ||
}} | ||
> | ||
<img | ||
className="h-10 w-10 self-center rounded-md md:self-start" | ||
src={walletProvider?.icon || mobileIcon} | ||
alt={walletProvider?.name || "Wallet"} | ||
/> | ||
<div className="ml-3 grow"> | ||
<p className="text-sm font-medium text-gray-900 dark:text-gray-100"> | ||
{walletProvider?.name || "Mobile Wallet"} | ||
</p> | ||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400"> | ||
{isDesktop | ||
? "Pending request on your mobile device" | ||
: "Tap to view request in app"} | ||
</p> | ||
</div> | ||
|
||
<button | ||
className="ml-4 inline-flex rounded-full md:border-transparent text-gray-400 | ||
hover:text-gray-500 focus:border-transparent focus:ring-0 self-center | ||
md:self-start p-3 md:p-0 border-1 border-gray-200 bg-gray-100 md:bg-white | ||
dark:bg-gray-800 dark:border-gray-700 dark:text-gray-200 | ||
dark:hover:text-gray-300 dark:focus:border-transparent" | ||
onClick={e => { | ||
e.stopPropagation() | ||
clearTimeout(timer.current) | ||
setIsVisible(false) | ||
}} | ||
> | ||
<span className="sr-only">Close</span> | ||
<svg | ||
className="h-5 w-5" | ||
xmlns="http://www.w3.org/2000/svg" | ||
viewBox="0 0 20 20" | ||
fill="currentColor" | ||
aria-hidden="true" | ||
> | ||
<path | ||
fillRule="evenodd" | ||
d="M14.354 5.646a.5.5 0 00-.708 0L10 9.293 5.354 4.646a.5.5 0 00-.708.708L9.293 10l-4.647 4.646a.5.5 0 00.708.708L10 10.707l4.646 4.647a.5.5 0 00.708-.708L10.707 10l4.647-4.646a.5.5 0 000-.708z" | ||
/> | ||
</svg> | ||
</button> | ||
</div> | ||
) | ||
} |
52 changes: 52 additions & 0 deletions
52
packages/fcl-wc/src/ui/components/NotificationProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import {useEffect, useState} from "preact/hooks" | ||
import {ActiveNotificationInfo, NotificationInfo} from "../../types/types" | ||
import {Notification} from "./Notification" | ||
|
||
export class NotificationController { | ||
activeToast: ActiveNotificationInfo | null = null | ||
subs: ((toasts: ActiveNotificationInfo | null) => void)[] = [] | ||
id: number = 0 | ||
|
||
showToast(toast: NotificationInfo) { | ||
const newToast = {...toast, id: this.id++} | ||
this.activeToast = newToast | ||
this.subs.forEach(sub => sub(newToast)) | ||
return { | ||
close: () => { | ||
if (this.activeToast?.id === newToast.id) { | ||
this.activeToast = null | ||
this.subs.forEach(sub => sub(null)) | ||
} | ||
}, | ||
} | ||
} | ||
|
||
subscribe(sub: (toasts: ActiveNotificationInfo | null) => void) { | ||
this.subs.push(sub) | ||
return () => { | ||
this.subs = this.subs.filter(s => s !== sub) | ||
} | ||
} | ||
} | ||
|
||
export function NotificationProvider({ | ||
controller, | ||
}: { | ||
controller: NotificationController | ||
}) { | ||
const [activeToast, setActiveToast] = useState(controller.activeToast) | ||
|
||
useEffect(() => { | ||
// We need this statement because we can miss events if the toasts are updated before the effect runs | ||
setActiveToast(controller.activeToast) | ||
|
||
const unsub = controller.subscribe(activeToast => { | ||
setActiveToast(activeToast) | ||
}) | ||
return () => unsub() | ||
}, []) | ||
|
||
if (!activeToast) return null | ||
|
||
return <Notification {...activeToast} key={activeToast.id} /> | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import {render} from "preact" | ||
import styles from "./styles.css" | ||
import { | ||
NotificationController, | ||
NotificationProvider, | ||
} from "./components/NotificationProvider" | ||
|
||
let notificationController: NotificationController | null = null | ||
|
||
export function getNotificationController() { | ||
if (!notificationController) { | ||
notificationController = new NotificationController() | ||
createNotificationProvider(notificationController) | ||
} | ||
|
||
return notificationController | ||
} | ||
|
||
function createNotificationProvider(controller: NotificationController) { | ||
const shadowHost = document.createElement("div") | ||
const shadowRoot = shadowHost.attachShadow({mode: "open"}) | ||
const container = document.createElement("div") | ||
|
||
shadowRoot.appendChild(container) | ||
document.body.appendChild(shadowHost) | ||
|
||
const style = document.createElement("style") | ||
style.textContent = styles | ||
shadowRoot.appendChild(style) | ||
|
||
// Subscribe to root dark mode changes to inherit the theme | ||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)") | ||
const listener = () => { | ||
container.classList.toggle("dark", mediaQuery.matches) | ||
} | ||
mediaQuery.addEventListener("change", listener) | ||
|
||
render(<NotificationProvider controller={controller} />, container) | ||
} |
Oops, something went wrong.