Skip to content

Commit

Permalink
stash
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink committed Oct 27, 2024
1 parent cc2f3f9 commit f7cd390
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 230 deletions.
12 changes: 2 additions & 10 deletions packages/fcl-wc/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {FLOW_METHODS, REQUEST_TYPES} from "./constants"
import {SignClient} from "@walletconnect/sign-client/dist/types/client"
import {createSessionProposal, request} from "./session"
import {config} from "@onflow/config"
import {showToast} from "./ui/toaster"
import {getNotificationController} from "./ui/notifications"

type WalletConnectModalType =
typeof import("@walletconnect/modal").WalletConnectModal
Expand Down Expand Up @@ -153,16 +153,8 @@ const makeExec = (
const appTitle: string | undefined = await config().get("app.detail.title")
const appIcon: string | undefined = await config().get("app.detail.icon")

const {close: closePrompt} = showToast({
const {close: closePrompt} = getNotificationController().showToast({
walletProvider: authnService?.provider,
appInfo: {
name: appTitle,
icon: appIcon,
},
onDeeplink: () => {
// todo: refactor
openDeeplink(service.uid)
},
})

return await exec({
Expand Down
8 changes: 8 additions & 0 deletions packages/fcl-wc/src/types/types.ts
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}
78 changes: 78 additions & 0 deletions packages/fcl-wc/src/ui/components/Notification.tsx
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 packages/fcl-wc/src/ui/components/NotificationProvider.tsx
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} />
}
153 changes: 0 additions & 153 deletions packages/fcl-wc/src/ui/components/Toaster.tsx

This file was deleted.

39 changes: 39 additions & 0 deletions packages/fcl-wc/src/ui/notifications.tsx
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)
}
Loading

0 comments on commit f7cd390

Please sign in to comment.