Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add global payment switch #773

Merged
merged 16 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"notMonetized_text_unsupportedScheme": {
"message": "Web Monetization only works with websites using https://."
},
"app_text_disabled": {
"message": "The extension is disabled. Click the power icon above to enable the extension and continue making payments."
},
"keyRevoked_error_title": {
"message": "Unauthorized access to the wallet."
},
Expand Down
6 changes: 6 additions & 0 deletions src/background/services/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ export class Background {
return;
}

case 'TOGGLE_PAYMENTS': {
await this.monetizationService.togglePayments();
await this.updateVisualIndicatorsForCurrentTab();
return;
}

case 'UPDATE_RATE_OF_PAY':
return success(
await this.storage.updateRate(message.payload.rateOfPay),
Expand Down
47 changes: 40 additions & 7 deletions src/background/services/monetization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ export class MonetizationService {
const {
state,
continuousPaymentsEnabled,
enabled,
rateOfPay,
connected,
walletAddress: connectedWallet,
} = await this.storage.get([
'state',
'continuousPaymentsEnabled',
'enabled',
'connected',
'rateOfPay',
'walletAddress',
Expand Down Expand Up @@ -133,7 +135,11 @@ export class MonetizationService {
const isAdjusted = await this.adjustSessionsAmount(sessionsArr, rate);
if (!isAdjusted) return;

if (continuousPaymentsEnabled && this.canTryPayment(connected, state)) {
if (
enabled &&
continuousPaymentsEnabled &&
this.canTryPayment(connected, state)
) {
sessionsArr.forEach((session) => {
if (!sessions.get(session.id)) return;
const source = replacedSessions.has(session.id)
Expand Down Expand Up @@ -213,14 +219,20 @@ export class MonetizationService {
return;
}

const { state, connected, continuousPaymentsEnabled } =
const { state, connected, continuousPaymentsEnabled, enabled } =
await this.storage.get([
'state',
'connected',
'continuousPaymentsEnabled',
'enabled',
]);
if (!continuousPaymentsEnabled || !this.canTryPayment(connected, state))
if (
!enabled ||
!continuousPaymentsEnabled ||
!this.canTryPayment(connected, state)
) {
return;
}

payload.forEach((p) => {
const { requestId } = p;
Expand All @@ -236,14 +248,20 @@ export class MonetizationService {
return;
}

const { state, connected, continuousPaymentsEnabled } =
const { state, connected, continuousPaymentsEnabled, enabled } =
await this.storage.get([
'state',
'connected',
'continuousPaymentsEnabled',
'enabled',
]);
if (!continuousPaymentsEnabled || !this.canTryPayment(connected, state))
if (
!enabled ||
!continuousPaymentsEnabled ||
!this.canTryPayment(connected, state)
) {
return;
}

for (const session of sessions.values()) {
session.resume();
Expand All @@ -257,12 +275,27 @@ export class MonetizationService {
}

async toggleWM() {
sidvishnoi marked this conversation as resolved.
Show resolved Hide resolved
const { continuousPaymentsEnabled } = await this.storage.get([
const { continuousPaymentsEnabled, enabled } = await this.storage.get([
'continuousPaymentsEnabled',
'enabled',
]);
const nowEnabled = !continuousPaymentsEnabled;
await this.storage.set({ continuousPaymentsEnabled: nowEnabled });
if (nowEnabled) {
if (nowEnabled && enabled) {
sidvishnoi marked this conversation as resolved.
Show resolved Hide resolved
await this.resumePaymentSessionActiveTab();
} else {
this.stopAllSessions();
}
}

async togglePayments() {
const { continuousPaymentsEnabled, enabled } = await this.storage.get([
'continuousPaymentsEnabled',
'enabled',
]);
const nowEnabled = !enabled;
await this.storage.set({ enabled: nowEnabled });
if (nowEnabled && continuousPaymentsEnabled) {
await this.resumePaymentSessionActiveTab();
} else {
this.stopAllSessions();
Expand Down
8 changes: 0 additions & 8 deletions src/background/services/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,6 @@ export class StorageService {
return data as unknown as Storage;
}

async getWMState(): Promise<boolean> {
const { continuousPaymentsEnabled } = await this.get([
'continuousPaymentsEnabled',
]);

return continuousPaymentsEnabled;
}

sidvishnoi marked this conversation as resolved.
Show resolved Hide resolved
async keyPairExists(): Promise<boolean> {
const keys = await this.get(['privateKey', 'publicKey', 'keyId']);
if (
Expand Down
8 changes: 7 additions & 1 deletion src/background/services/tabEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,16 @@ export class TabEvents {
updateVisualIndicators = async (tab: Tabs.Tab) => {
const tabInfo = this.tabState.getPopupTabData(tab);
this.sendToPopup.send('SET_TAB_DATA', tabInfo);
const { continuousPaymentsEnabled, connected, state } =
const { continuousPaymentsEnabled, enabled, connected, state } =
await this.storage.get([
'continuousPaymentsEnabled',
'enabled',
'connected',
'state',
]);
const { path, title } = this.getIconAndTooltip({
continuousPaymentsEnabled,
enabled,
connected,
state,
tabInfo,
Expand Down Expand Up @@ -173,11 +175,13 @@ export class TabEvents {

private getIconAndTooltip({
continuousPaymentsEnabled,
enabled,
connected,
state,
tabInfo,
}: {
continuousPaymentsEnabled: Storage['continuousPaymentsEnabled'];
enabled: Storage['enabled'];
connected: Storage['connected'];
state: Storage['state'];
tabInfo: PopupTabInfo;
Expand All @@ -186,6 +190,8 @@ export class TabEvents {
let iconData = ICONS.default;
if (!connected) {
// use defaults
} else if (!enabled) {
iconData = ICONS.default_gray;
} else if (!isOkState(state) || tabInfo.status === 'all_sessions_invalid') {
iconData = continuousPaymentsEnabled
? ICONS.enabled_warn
Expand Down
39 changes: 39 additions & 0 deletions src/pages/popup/components/PowerSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Power } from '@/pages/shared/components/Icons';
import { cn } from '@/shared/helpers';

export const PowerSwitch = ({
enabled,
onChange,
title,
className,
iconClassName = 'size-6',
}: {
enabled: boolean;
onChange: () => void;
title: string;
className?: string;
iconClassName?: string;
}) => {
return (
<label
className={cn(
'group my-0 cursor-pointer rounded-full p-0.5 transition-colors focus-within:shadow',
className,
enabled
? 'text-gray-500 focus-within:text-error hover:text-error'
: 'text-gray-300 focus-within:text-secondary-dark hover:text-secondary-dark',
)}
title={title}
>
<input
type="checkbox"
checked={enabled}
onChange={onChange}
aria-label={title}
className="sr-only"
/>
<Power className={iconClassName} />
</label>
);
};
26 changes: 26 additions & 0 deletions src/pages/popup/components/TogglePaymentsButton.tsx
sidvishnoi marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { PowerSwitch } from '@/popup/components/PowerSwitch';
import { useMessage } from '@/popup/lib/context';
import { dispatch } from '@/popup/lib/store';

export const TogglePaymentsButton = ({
large = false,
enabled = false,
}: {
large?: boolean;
enabled?: boolean;
}) => {
const message = useMessage();

return (
<PowerSwitch
enabled={enabled}
onChange={() => {
message.send('TOGGLE_PAYMENTS');
dispatch({ type: 'TOGGLE_PAYMENTS' });
}}
title={enabled ? 'Disable extension' : 'Enable extension'}
iconClassName={large ? 'w-32' : 'w-6'}
/>
);
};
3 changes: 3 additions & 0 deletions src/pages/popup/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { ArrowBack, Settings } from '@/pages/shared/components/Icons';
import { HeaderEmpty } from './HeaderEmpty';
import { TogglePaymentsButton } from '@/popup/components/TogglePaymentsButton';
import { ROUTES_PATH } from '@/popup/Popup';
import { useBrowser } from '@/popup/lib/context';
import { usePopupState } from '@/popup/lib/store';
Expand Down Expand Up @@ -35,10 +36,12 @@ const NavigationButton = () => {

export const Header = () => {
const browser = useBrowser();
const { enabled } = usePopupState();
const Logo = browser.runtime.getURL('assets/images/logo.svg');

return (
<HeaderEmpty logo={Logo}>
{enabled && <TogglePaymentsButton enabled={true} />}
<NavigationButton />
</HeaderEmpty>
);
Expand Down
5 changes: 5 additions & 0 deletions src/pages/popup/lib/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export const dispatch = ({ type, data }: Actions) => {
store.continuousPaymentsEnabled = !store.continuousPaymentsEnabled;
return;
}
case 'TOGGLE_PAYMENTS': {
store.enabled = !store.enabled;
return;
}
case 'SET_CONNECTED':
store.connected = data.connected;
break;
Expand All @@ -52,6 +56,7 @@ export const dispatch = ({ type, data }: Actions) => {
type Actions =
| { type: 'SET_DATA'; data: PopupState }
| { type: 'TOGGLE_WM'; data?: never }
| { type: 'TOGGLE_PAYMENTS'; data?: never }
| { type: 'SET_CONNECTED'; data: { connected: boolean } }
| { type: 'UPDATE_RATE_OF_PAY'; data: { rateOfPay: string } }
| BackgroundToPopupMessage;
14 changes: 13 additions & 1 deletion src/pages/popup/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@ import { Settings } from '@/pages/shared/components/Icons';
import { formatNumber, roundWithPrecision } from '@/pages/shared/lib/utils';
import { PayWebsiteForm } from '@/popup/components/PayWebsiteForm';
import { NotMonetized } from '@/popup/components/NotMonetized';
import { TogglePaymentsButton } from '@/popup/components/TogglePaymentsButton';
import { useTranslation } from '@/popup/lib/context';
import { usePopupState } from '@/popup/lib/store';

export const Component = () => {
const t = useTranslation();
const { tab } = usePopupState();
const { tab, enabled } = usePopupState();

if (!enabled) {
return (
<div className="flex h-full flex-col items-center justify-center gap-4 text-center">
<TogglePaymentsButton enabled={false} large={true} />
<p className="text-center text-lg leading-tight text-medium">
{t('app_text_disabled')}
</p>
</div>
);
}

if (tab.status !== 'monetized') {
switch (tab.status) {
Expand Down
16 changes: 16 additions & 0 deletions src/pages/shared/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,22 @@ export const Settings = (props: React.SVGProps<SVGSVGElement>) => {
);
};

export const Power = (props: React.SVGProps<SVGSVGElement>) => {
return (
<svg
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M5.636 5.636a9 9 0 1 0 12.728 0M12 3v9" />
</svg>
);
};

export const DollarSign = (props: React.SVGProps<SVGSVGElement>) => {
return (
<svg
Expand Down
4 changes: 4 additions & 0 deletions src/shared/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ export type PopupToBackgroundMessage = {
input: never;
output: never;
};
TOGGLE_PAYMENTS: {
input: never;
output: never;
};
PAY_WEBSITE: {
input: PayWebsitePayload;
output: PayWebsiteResponse;
Expand Down