From 565eb254d6ecf1a89a60b6a57ce5f969cdc952d2 Mon Sep 17 00:00:00 2001 From: fbwoolf Date: Wed, 23 Jun 2021 19:35:02 -0500 Subject: [PATCH] refactor: redirect --- .changeset/wicked-mails-grab.md | 2 +- scripts/generate-manifest.js | 2 +- src/background/background.ts | 52 +++++++------------------ src/background/vault-types.ts | 7 +++- src/background/vault.ts | 2 +- src/common/hooks/use-vault-messenger.ts | 2 +- src/common/message-types.ts | 4 +- src/pages/set-password.tsx | 2 +- test-app/src/common/context.ts | 4 +- test-app/src/common/use-auth.ts | 15 +++---- test-app/src/components/app.tsx | 16 +------- test-app/src/components/auth.tsx | 7 +--- 12 files changed, 37 insertions(+), 78 deletions(-) diff --git a/.changeset/wicked-mails-grab.md b/.changeset/wicked-mails-grab.md index 7a1c013e616..54d67caf33d 100644 --- a/.changeset/wicked-mails-grab.md +++ b/.changeset/wicked-mails-grab.md @@ -2,4 +2,4 @@ '@stacks/wallet-web': minor --- -This adds using the app url to redirect and reload the app after the extension is installed. +This adds a redirect to the app installing the extension after a user sets their password. diff --git a/scripts/generate-manifest.js b/scripts/generate-manifest.js index 74f0623a287..be2af780778 100644 --- a/scripts/generate-manifest.js +++ b/scripts/generate-manifest.js @@ -6,7 +6,7 @@ const manifest = { author: 'Hiro PBC', description: 'Stacks Wallet. Use the Stacks blockchain to access privacy-friendly apps, and keep data in your control.', - permissions: ['tabs'], + permissions: ['tabs', 'storage'], manifest_version: 2, background: { scripts: ['background.js'], diff --git a/src/background/background.ts b/src/background/background.ts index fd47e775e91..4e4e5c852e7 100755 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -11,8 +11,6 @@ import { CONTENT_SCRIPT_PORT, ExternalMethods, MessageFromContentScript, - InternalMethods, - MESSAGE_SOURCE, } from '@common/message-types'; import type { VaultActions } from '@background/vault-types'; @@ -21,31 +19,19 @@ import { vaultMessageHandler } from '@background/vault'; const IS_TEST_ENV = process.env.TEST_ENV === 'true'; -function setAppParamOfSourceInstallingExtension() { - // The app url is saved for use after onboarding - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString); - const appUrl = urlParams.get('app'); - // Left for testing only - // const appUrl = 'http://localhost:3000/'; - if (appUrl) window.localStorage.setItem('appUrl', appUrl); -} - -function getAppParamOfSourceInstallingExtension() { - const appUrl = window.localStorage.getItem('appUrl') || undefined; - return appUrl; -} +const CHROME_STORE_URL = + 'https://chrome.google.com/webstore/detail/stacks-wallet/ldinpeekobnhjjdofggfgjlcehhmanlj/'; +const FIREFOX_STORE_URL = 'https://addons.mozilla.org/en-US/firefox/addon/stacks-wallet/'; // Listen for install event chrome.runtime.onInstalled.addListener(async details => { if (details.reason === 'install' && !IS_TEST_ENV) { - setAppParamOfSourceInstallingExtension(); - const appUrl = getAppParamOfSourceInstallingExtension(); - // Reload the app tab right after install to detect the extension chrome.tabs.query({ lastFocusedWindow: true }, async tabs => { - const appTab = tabs?.find(tab => tab.url === appUrl); + const appTab = tabs?.find( + tab => tab.url === CHROME_STORE_URL || tab.url === FIREFOX_STORE_URL + ); if (appTab?.id) { - await chrome.tabs.reload(appTab?.id); + await chrome.tabs.remove(appTab.id); } }); await chrome.tabs.create({ @@ -92,22 +78,12 @@ chrome.runtime.onConnect.addListener(port => { } }); -function redirectToAppTab() { - const appUrl = getAppParamOfSourceInstallingExtension(); +function goBackToApp() { chrome.tabs.query({ lastFocusedWindow: true }, async tabs => { - const appTab = tabs?.find(tab => tab.url === appUrl); - if (appTab?.id) { - await chrome.tabs.update(appTab?.id, { active: true }); - chrome.tabs.sendMessage(appTab?.id, { - source: MESSAGE_SOURCE, - method: InternalMethods.completeOnboarding, - payload: undefined, - }); - } else { - // Only open a new tab if the user has closed the app tab - await chrome.tabs.create({ - url: appUrl, - }); + const appTabId = tabs[tabs.length - 2].id; + if (appTabId) { + await chrome.tabs.update(appTabId, { active: true }); + await chrome.tabs.reload(appTabId); } }); } @@ -117,9 +93,9 @@ chrome.runtime.onMessage.addListener((message: VaultActions, sender, sendRespons // Only respond to internal messages from our UI, not content scripts in other applications if (!sender.url?.startsWith(chrome.runtime.getURL(''))) return; // Go back to app tab after setting password - if (message.method === 'completeOnboarding') { + if (message.method === 'redirectAfterSetPassword') { // Delay redirect for a smoother transition - setTimeout(() => redirectToAppTab(), 500); + setTimeout(() => goBackToApp(), 500); } void vaultMessageHandler(message).then(sendResponse).catch(sendResponse); // Return true to specify that we are responding async diff --git a/src/background/vault-types.ts b/src/background/vault-types.ts index 31c83ad058e..53b6c1bf243 100644 --- a/src/background/vault-types.ts +++ b/src/background/vault-types.ts @@ -17,7 +17,10 @@ export type SetPassword = VaultMessage; export type UnlockWallet = VaultMessage; export type LockWallet = VaultMessage; export type SwitchAccount = VaultMessage; -export type CompleteOnboarding = VaultMessage; +export type RedirectAfterSetPassword = VaultMessage< + InternalMethods.redirectAfterSetPassword, + undefined +>; export type VaultActions = | GetWallet @@ -29,4 +32,4 @@ export type VaultActions = | UnlockWallet | SwitchAccount | LockWallet - | CompleteOnboarding; + | RedirectAfterSetPassword; diff --git a/src/background/vault.ts b/src/background/vault.ts index efe9728d37a..dd01bf4eaa0 100644 --- a/src/background/vault.ts +++ b/src/background/vault.ts @@ -98,7 +98,7 @@ function throwUnhandledMethod(message: VaultActions) { // Reducer to manage the state of the vault export const vaultReducer = async (message: VaultActions): Promise => { switch (message.method) { - case InternalMethods.completeOnboarding: + case InternalMethods.redirectAfterSetPassword: return { ...inMemoryVault, hasCompletedOnboarding: true, diff --git a/src/common/hooks/use-vault-messenger.ts b/src/common/hooks/use-vault-messenger.ts index 1990a1258c9..622e8e7c3b5 100644 --- a/src/common/hooks/use-vault-messenger.ts +++ b/src/common/hooks/use-vault-messenger.ts @@ -115,7 +115,7 @@ export const useVaultMessenger = () => { const doCompleteOnboarding = () => innerMessageWrapper({ - method: InternalMethods.completeOnboarding, + method: InternalMethods.redirectAfterSetPassword, payload: undefined, }); diff --git a/src/common/message-types.ts b/src/common/message-types.ts index fa9d8ea8888..d8f78c8c669 100644 --- a/src/common/message-types.ts +++ b/src/common/message-types.ts @@ -22,7 +22,7 @@ export enum InternalMethods { unlockWallet = 'unlockWallet', lockWallet = 'lockWallet', switchAccount = 'switchAccount', - completeOnboarding = 'completeOnboarding', + redirectAfterSetPassword = 'redirectAfterSetPassword', } export type ExtensionMethods = ExternalMethods | InternalMethods; @@ -63,7 +63,7 @@ export type TransactionResponseMessage = Message< >; export type CompleteOnboardingResponseMessage = Message< - InternalMethods.completeOnboarding, + InternalMethods.redirectAfterSetPassword, undefined >; diff --git a/src/pages/set-password.tsx b/src/pages/set-password.tsx index 9f5c41e6a2e..444ebf8a0eb 100644 --- a/src/pages/set-password.tsx +++ b/src/pages/set-password.tsx @@ -72,7 +72,7 @@ export const SetPasswordPage: React.FC = ({ if (strengthResult.meetsAllStrengthRequirements) { await submit(password); // Sends message to background script - void doCompleteOnboarding(); + await doCompleteOnboarding(); return; } setLoading(false); diff --git a/test-app/src/common/context.ts b/test-app/src/common/context.ts index 35625f24dce..4b6a1c19bc7 100644 --- a/test-app/src/common/context.ts +++ b/test-app/src/common/context.ts @@ -3,7 +3,6 @@ import { UserSession, AppConfig, UserData } from '@stacks/auth'; export interface AppState { userData: UserData | null; - isOnboarding: boolean; } export const defaultState = (): AppState => { @@ -13,10 +12,9 @@ export const defaultState = (): AppState => { if (userSession.isUserSignedIn()) { return { userData: userSession.loadUserData(), - isOnboarding: false, }; } - return { userData: null, isOnboarding: false }; + return { userData: null }; }; export const AppContext = createContext(defaultState()); diff --git a/test-app/src/common/use-auth.ts b/test-app/src/common/use-auth.ts index db2b089b681..26c3af75b6f 100644 --- a/test-app/src/common/use-auth.ts +++ b/test-app/src/common/use-auth.ts @@ -1,10 +1,10 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import { AppState, defaultState } from '@common/context'; import { AppConfig, UserSession } from '@stacks/auth'; import { AuthOptions } from '@stacks/connect'; export function useAuth() { - const [state, setState] = useState(defaultState()); + const [state, setState] = React.useState(defaultState()); const [authResponse, setAuthResponse] = React.useState(''); const [appPrivateKey, setAppPrivateKey] = React.useState(''); @@ -14,17 +14,15 @@ export function useAuth() { ); const userSession = useMemo(() => new UserSession({ appConfig }), [appConfig]); - const handleIsOnboarding = (isOnboarding: boolean) => setState({ ...state, isOnboarding }); - const handleSignOut = useCallback(() => { userSession.signUserOut(); - setState({ ...state, userData: null, isOnboarding: false }); + setState({ userData: null }); }, [userSession]); const handleRedirectAuth = useCallback(async () => { if (userSession.isSignInPending()) { const userData = await userSession.handlePendingSignIn(); - setState({ ...state, userData, isOnboarding: false }); + setState({ userData }); setAppPrivateKey(userData.appPrivateKey); } else if (userSession.isUserSignedIn()) { setAppPrivateKey(userSession.loadUserData().appPrivateKey); @@ -35,7 +33,7 @@ export function useAuth() { const userData = userSession.loadUserData(); setAppPrivateKey(userSession.loadUserData().appPrivateKey); setAuthResponse(authResponse); - setState({ ...state, userData, isOnboarding: false }); + setState({ userData }); }, []); const onCancel = useCallback(() => { @@ -46,7 +44,7 @@ export function useAuth() { void handleRedirectAuth(); if (userSession.isUserSignedIn() && !state.userData) { const userData = userSession.loadUserData(); - setState({ ...state, userData, isOnboarding: false }); + setState({ userData }); } }, [handleRedirectAuth, userSession, state]); @@ -67,6 +65,5 @@ export function useAuth() { authResponse, appPrivateKey, handleSignOut, - handleIsOnboarding, }; } diff --git a/test-app/src/components/app.tsx b/test-app/src/components/app.tsx index 027270d3363..0f9c44d360e 100755 --- a/test-app/src/components/app.tsx +++ b/test-app/src/components/app.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { ThemeProvider, theme, Flex, ColorModeProvider } from '@stacks/ui'; import { Connect } from '@stacks/connect-react'; import { AppContext } from '@common/context'; @@ -9,19 +9,7 @@ import { useAuth } from '@common/use-auth'; import { GlobalStyles } from '@components/global-styles'; export const App: React.FC = () => { - const { authOptions, state, authResponse, appPrivateKey, handleSignOut, handleIsOnboarding } = - useAuth(); - - const handleMessage = (event: MessageEvent) => { - if (event.data.method === 'completeOnboarding') { - handleIsOnboarding(true); - } - }; - - useEffect(() => { - window.addEventListener('message', handleMessage); - return () => window.removeEventListener('message', handleMessage); - }, []); + const { authOptions, state, authResponse, appPrivateKey, handleSignOut } = useAuth(); return ( diff --git a/test-app/src/components/auth.tsx b/test-app/src/components/auth.tsx index bf617e2d01f..dca5713193f 100644 --- a/test-app/src/components/auth.tsx +++ b/test-app/src/components/auth.tsx @@ -1,12 +1,9 @@ -import React, { useContext } from 'react'; +import React from 'react'; import { Button, Text, Box, ButtonGroup } from '@stacks/ui'; import { useConnect } from '@stacks/connect-react'; -import { AppContext } from '@common/context'; export const Auth: React.FC = () => { - const state = useContext(AppContext); const { doOpenAuth } = useConnect(); - return ( @@ -14,7 +11,7 @@ export const Auth: React.FC = () => {