Skip to content

Commit

Permalink
feat: popup amount notation (#226)
Browse files Browse the repository at this point in the history
* Fetch exchange rates and convert rate of pay

* Fix conversion

* Fetch rate of pay from storage

* Amount conversion

* Rename to `rateOfPay`

* Rename to `rateOfPay`
  • Loading branch information
raducristianpopa authored Apr 16, 2024
1 parent bf0ecc0 commit 930c0eb
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 135 deletions.
7 changes: 7 additions & 0 deletions src/background/services/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ export class Background {
)
throw new Error('Not implemented')

case PopupToBackgroundAction.UPDATE_RATE_OF_PAY:
return success(
await this.storage.set({
rateOfPay: message.payload.rateOfPay
})
)

default:
return
}
Expand Down
55 changes: 6 additions & 49 deletions src/background/services/storage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { DEFAULT_RATE_OF_PAY, DEFAULT_INTERVAL_MS } from '@/background/config'
import { Logger } from '@/shared/logger'
import type {
PopupStore,
Storage,
StorageKey,
WebsiteData
} from '@/shared/types'
import type { PopupStore, Storage, StorageKey } from '@/shared/types'
import { type Browser } from 'webextension-polyfill'

const defaultStorage = {
Expand Down Expand Up @@ -52,58 +47,20 @@ export class StorageService {
}
}

// TODO: Exception list (post-v1) - return data for the current website
async getPopupData(): Promise<PopupStore> {
// TODO: Improve URL management
const [{ url: tabUrl }] = await this.browser.tabs.query({
active: true,
currentWindow: true
})
const data = await this.get([
'enabled',
'connected',
'amount',
'exceptionList',
'rateOfPay',
'minRateOfPay',
'maxRateOfPay',
'walletAddress',
'publicKey'
])

const website: WebsiteData = {
url: '',
amount: { value: '0', interval: DEFAULT_INTERVAL_MS }
}

if (tabUrl) {
let url = ''
try {
const parsedUrl = new URL(tabUrl)
if (parsedUrl.protocol !== 'https:') {
throw new Error('Only https websites allowed')
}
url = `${parsedUrl.origin}${parsedUrl.pathname}`
} catch (e) {
this.logger.error(e.message)
/** noop */
}

website.url = url
if (data.exceptionList && data.exceptionList[url]) {
website.amount = data.exceptionList[url]
} else {
website.amount = {
value: DEFAULT_RATE_OF_PAY,
interval: DEFAULT_INTERVAL_MS
}
}
}

return {
enabled: data.enabled,
connected: data.connected,
amount: data.amount,
walletAddress: data.walletAddress,
publicKey: data.publicKey,
website
}
return data
}

async keyPairExists(): Promise<boolean> {
Expand Down
4 changes: 2 additions & 2 deletions src/popup/components/PayWebsiteForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface PayWebsiteFormProps {

export const PayWebsiteForm = () => {
const {
state: { walletAddress, website }
state: { walletAddress }
} = React.useContext(PopupStateContext)
const {
register,
Expand All @@ -34,7 +34,7 @@ export const PayWebsiteForm = () => {
addOn={getCurrencySymbol(walletAddress.assetCode)}
label={
<div>
Pay <span className="text-primary">{website.url}</span>
Pay <span className="text-primary">URL</span>
</div>
}
placeholder="0.00"
Expand Down
21 changes: 19 additions & 2 deletions src/popup/lib/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { DeepNonNullable, PopupStore } from '@/shared/types'

export enum ReducerActionType {
SET_DATA = 'SET_DATA',
TOGGLE_WM = 'TOGGLE_WM'
TOGGLE_WM = 'TOGGLE_WM',
UPDATE_RATE_OF_PAY = 'UPDATE_RATE_OF_PAY'
}

export type PopupState = Required<DeepNonNullable<PopupStore>>
Expand All @@ -28,7 +29,17 @@ interface ToggleWMAction extends ReducerActionMock {
type: ReducerActionType.TOGGLE_WM
}

export type ReducerActions = SetDataAction | ToggleWMAction
interface UpdateRateOfPayAction extends ReducerActionMock {
type: ReducerActionType.UPDATE_RATE_OF_PAY
data: {
rateOfPay: string
}
}

export type ReducerActions =
| SetDataAction
| ToggleWMAction
| UpdateRateOfPayAction

export const PopupStateContext = React.createContext<PopupContext>(
{} as PopupContext
Expand All @@ -45,6 +56,12 @@ const reducer = (state: PopupState, action: ReducerActions): PopupState => {
enabled: !state.enabled
}
}
case ReducerActionType.UPDATE_RATE_OF_PAY: {
return {
...state,
rateOfPay: action.data.rateOfPay
}
}
default:
return state
}
Expand Down
9 changes: 9 additions & 0 deletions src/popup/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export const toggleWM = async () => {
})
}

export const updateRateOfPay = async (
payload: PopupToBackgroundActionPayload[PopupToBackgroundAction.UPDATE_RATE_OF_PAY]
) => {
return await message.send({
action: PopupToBackgroundAction.UPDATE_RATE_OF_PAY,
payload
})
}

export const payWebsite = async (
payload: PopupToBackgroundActionPayload[PopupToBackgroundAction.PAY_WEBSITE]
) => {
Expand Down
5 changes: 5 additions & 0 deletions src/popup/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ export const transformBalance = (amount: string, scale: number): string => {
export function charIsNumber(char?: string) {
return !!(char || '').match(/\d|\./)
}

export function roundWithPrecision(num: number, precision: number) {
const multiplier = Math.pow(10, precision)
return Math.round(num * multiplier) / multiplier
}
134 changes: 57 additions & 77 deletions src/popup/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
import React from 'react'
import { PopupStateContext } from '@/popup/lib/context'
import { PopupStateContext, ReducerActionType } from '@/popup/lib/context'
import { WarningSign } from '@/popup/components/Icons'
import { PayWebsiteForm } from '@/popup/components/PayWebsiteForm'
import { Slider } from '../components/ui/Slider'
import { updateRateOfPay } from '../lib/messages'
import { Label } from '../components/ui/Label'
import { getCurrencySymbol, roundWithPrecision } from '../lib/utils'

export const Component = () => {
const {
state: { enabled, website }
state: {
enabled,
rateOfPay,
minRateOfPay,
maxRateOfPay,
walletAddress
},
dispatch
} = React.useContext(PopupStateContext)

const rate = React.useMemo(() => {
const r = Number(rateOfPay) / 10 ** walletAddress.assetScale
if (roundWithPrecision(r, 2) > 0) {
return r.toFixed(2)
}

return r.toExponential()
}, [rateOfPay, walletAddress.assetScale])

// TODO: Use a debounce
const onRateChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const rateOfPay = event.currentTarget.value
const response = await updateRateOfPay({
rateOfPay
})
if (!response.success) return
dispatch({
type: ReducerActionType.UPDATE_RATE_OF_PAY,
data: {
rateOfPay
}
})
}

if (!enabled) {
return (
<div className="flex items-center gap-2">
Expand All @@ -19,79 +53,25 @@ export const Component = () => {
)
}

if (website.url === '') {
return (
<div className="flex items-center gap-2">
<WarningSign />
<p className="text-base text-medium">
This website does not support Web Monetization.
</p>
return (
<div className="space-y-4">
<div className="space-y-2">
<Label className="px-2 text-base font-medium text-medium">
Current rate of pay
</Label>
<Slider
onChange={onRateChange}
min={Number(minRateOfPay)}
max={Number(maxRateOfPay)}
step={Number(minRateOfPay)}
value={Number(rateOfPay)}
/>
<div className="flex w-full items-center justify-between px-2">
<span className="text-sm">
{rate} {getCurrencySymbol(walletAddress.assetCode)} per hour
</span>
</div>
</div>
)
}

return <PayWebsiteForm />
// const {
// data: { wmEnabled, rateOfPay, amount, amountType },
// setData,
// } = usePopup()
// const [tipAmount, setTipAmount] = useState('')
// const updateRateOfPay = async (event: any) => {
// setData(prevState => ({ ...prevState, rateOfPay: event.target.value }))
// }
// const updateStreamType = async (event: any) => {
// setData(prevState => ({
// ...prevState,
// amountType: { ...prevState.amountType, recurring: event.target.checked },
// }))
// }
// if (!wmEnabled) {
// return (
// <div className="flex items-center gap-2">
// <WarningSign />
// <p className="text-base text-medium">Web Monetization has been turned off.</p>
// </div>
// )
// }
// return (
// <div className="flex flex-col gap-8 basis-auto justify-center">
// <div className="grid gap-4 w-full">
// <div className="px-2 text-base font-medium text-medium">Current rate of pay</div>
// <Slider
// min={0}
// max={1}
// step={0.01}
// value={rateOfPay}
// onChange={updateRateOfPay}
// disabled={!amountType.recurring}
// />
// <div className="px-2 flex items-center justify-between w-full">
// <span>{!amountType.recurring ? '0c' : formatCurrency(rateOfPay)} per hour</span>
// <span>Remaining balance: ${amount}</span>
// </div>
// </div>
// <div className="flex items-center gap-4 h-7">
// <Switch size="small" checked={amountType.recurring} onChange={updateStreamType} />
// <span className="text-medium text-base">Continuous payments stream</span>
// </div>
// <div className="h-px bg-nav-active" />
// <div className="flex flex-col gap-4">
// <Label className="text-base font-medium text-medium">
// Pay <span className="text-primary">https://alexlakatos.com/</span>
// </Label>
// <Input
// value={tipAmount}
// type="number"
// id="amount"
// name="amount"
// placeholder="0.00"
// onChange={event => setTipAmount(event.target.value)}
// icon={<DollarSign />}
// />
// </div>
// <Button aria-label="Send now" className="text-base font-medium">
// Send now
// </Button>
// </div>
// )
</div>
)
}
2 changes: 1 addition & 1 deletion src/popup/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react'

export const Component = () => {
const { state } = React.useContext(PopupStateContext)
console.log(state)

if (state.connected) {
return <WalletInformation info={state} />
} else {
Expand Down
8 changes: 7 additions & 1 deletion src/shared/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export enum PopupToBackgroundAction {
CONNECT_WALLET = 'CONNECT_WALLET',
DISCONNECT_WALLET = 'DISCONNECT_WALLET',
TOGGLE_WM = 'TOGGLE_WM',
PAY_WEBSITE = 'PAY_WEBSITE'
PAY_WEBSITE = 'PAY_WEBSITE',
UPDATE_RATE_OF_PAY = 'UPDATE_RATE_OF_PAY'
}

export interface ConnectWalletPayload {
Expand All @@ -39,12 +40,17 @@ export interface PayWebsitePayload {
amount: string
}

export interface UpdateRateOfPayPayload {
rateOfPay: string
}

export interface PopupToBackgroundActionPayload {
[PopupToBackgroundAction.GET_CONTEXT_DATA]: undefined
[PopupToBackgroundAction.CONNECT_WALLET]: ConnectWalletPayload
[PopupToBackgroundAction.DISCONNECT_WALLET]: undefined
[PopupToBackgroundAction.TOGGLE_WM]: undefined
[PopupToBackgroundAction.PAY_WEBSITE]: PayWebsitePayload
[PopupToBackgroundAction.UPDATE_RATE_OF_PAY]: UpdateRateOfPayPayload
}

export type PopupToBackgroundMessage = {
Expand Down
4 changes: 1 addition & 3 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ export type StorageKey = keyof Storage
export type PopupStore = Omit<
Storage,
'privateKey' | 'keyId' | 'exceptionList' | 'token' | 'grant'
> & {
website: WebsiteData
}
>

export type DeepNonNullable<T> = {
[P in keyof T]?: NonNullable<T[P]>
Expand Down

0 comments on commit 930c0eb

Please sign in to comment.