Skip to content

Commit

Permalink
frontend/manage-accounts: add watchonly toggle per account
Browse files Browse the repository at this point in the history
  • Loading branch information
benma committed Nov 8, 2023
1 parent 9b22316 commit 3e0cef0
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 0 deletions.
40 changes: 40 additions & 0 deletions backend/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,46 @@ func (backend *Backend) RenameAccount(accountCode accountsTypes.Code, name strin
return nil
}

// AccountSetWatch sets the account's persisted watch flag to `watch`. Set to `true` if the account
// should be loaded even if its keystore is not connected.
// If `watch` is set to `false`, the account is unloaded and the frontend notified.
func (backend *Backend) AccountSetWatch(accountCode accountsTypes.Code, watch bool) error {
err := backend.config.ModifyAccountsConfig(func(accountsConfig *config.AccountsConfig) error {
acct := accountsConfig.Lookup(accountCode)
if acct == nil {
return errp.Newf("Could not find account %s", accountCode)
}
acct.Watch = &watch
return nil
})
if err != nil {
return err
}
defer backend.accountsAndKeystoreLock.Lock()()

// ETH tokens inherit the Watch-flag from the parent ETH account.
// If the watch status of an ETH account was changed, we update it for its tokens as well.
//
// This ensures that removing a keystore after setting an ETH account including tokens to
// watchonly results in the account *and* the tokens remaining loaded.
if acct := backend.accounts.lookup(accountCode); acct != nil {
if acct.Config().Config.CoinCode == coinpkg.CodeETH {
for _, erc20TokenCode := range acct.Config().Config.ActiveTokens {
erc20AccountCode := Erc20AccountCode(accountCode, erc20TokenCode)
if tokenAcct := backend.accounts.lookup(erc20AccountCode); tokenAcct != nil {
tokenAcct.Config().Config.Watch = &watch
}
}
}
}

if !watch {
backend.initAccounts(false)
backend.emitAccountsStatusChanged()
}
return nil
}

// addAccount adds the given account to the backend.
// The accountsAndKeystoreLock must be held when calling this function.
func (backend *Backend) addAccount(account accounts.Interface) {
Expand Down
24 changes: 24 additions & 0 deletions backend/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type Backend interface {
SetAccountActive(accountCode accountsTypes.Code, active bool) error
SetTokenActive(accountCode accountsTypes.Code, tokenCode string, active bool) error
RenameAccount(accountCode accountsTypes.Code, name string) error
AccountSetWatch(accountCode accountsTypes.Code, watch bool) error
AOPP() backend.AOPP
AOPPCancel()
AOPPApprove()
Expand Down Expand Up @@ -202,6 +203,7 @@ func NewHandlers(
getAPIRouterNoError(apiRouter)("/set-account-active", handlers.postSetAccountActiveHandler).Methods("POST")
getAPIRouterNoError(apiRouter)("/set-token-active", handlers.postSetTokenActiveHandler).Methods("POST")
getAPIRouterNoError(apiRouter)("/rename-account", handlers.postRenameAccountHandler).Methods("POST")
getAPIRouterNoError(apiRouter)("/account-set-watch", handlers.postAccountSetWatchHandler).Methods("POST")
getAPIRouterNoError(apiRouter)("/accounts/reinitialize", handlers.postAccountsReinitializeHandler).Methods("POST")
getAPIRouter(apiRouter)("/account-summary", handlers.getAccountSummary).Methods("GET")
getAPIRouterNoError(apiRouter)("/supported-coins", handlers.getSupportedCoinsHandler).Methods("GET")
Expand Down Expand Up @@ -346,6 +348,7 @@ type accountJSON struct {
// keystore.
Keystore config.Keystore `json:"keystore"`
Active bool `json:"active"`
Watch bool `json:"watch"`
CoinCode coinpkg.Code `json:"coinCode"`
CoinUnit string `json:"coinUnit"`
CoinName string `json:"coinName"`
Expand All @@ -362,6 +365,7 @@ func newAccountJSON(keystore config.Keystore, account accounts.Interface, active
return &accountJSON{
Keystore: keystore,
Active: !account.Config().Config.Inactive,
Watch: account.Config().Config.IsWatch(),
CoinCode: account.Coin().Code(),
CoinUnit: account.Coin().Unit(false),
CoinName: account.Coin().Name(),
Expand Down Expand Up @@ -709,6 +713,26 @@ func (handlers *Handlers) postRenameAccountHandler(r *http.Request) interface{}
return response{Success: true}
}

func (handlers *Handlers) postAccountSetWatchHandler(r *http.Request) interface{} {
var jsonBody struct {
AccountCode accountsTypes.Code `json:"accountCode"`
Watch bool `json:"watch"`
}

type response struct {
Success bool `json:"success"`
ErrorMessage string `json:"errorMessage,omitempty"`
}

if err := json.NewDecoder(r.Body).Decode(&jsonBody); err != nil {
return response{Success: false, ErrorMessage: err.Error()}
}
if err := handlers.backend.AccountSetWatch(jsonBody.AccountCode, jsonBody.Watch); err != nil {
return response{Success: false, ErrorMessage: err.Error()}
}
return response{Success: true}
}

func (handlers *Handlers) postAccountsReinitializeHandler(_ *http.Request) interface{} {
handlers.backend.ReinitializeAccounts()
return nil
Expand Down
1 change: 1 addition & 0 deletions frontends/web/src/api/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type TKeystore = {
export interface IAccount {
keystore: TKeystore;
active: boolean;
watch: boolean;
coinCode: CoinCode;
coinUnit: string;
coinName: string;
Expand Down
4 changes: 4 additions & 0 deletions frontends/web/src/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const renameAccount = (accountCode: AccountCode, name: string): Promise<I
return apiPost('rename-account', { accountCode, name });
};

export const accountSetWatch = (accountCode: AccountCode, watch: boolean): Promise<ISuccess> => {
return apiPost('account-set-watch', { accountCode, watch });
};

export const reinitializeAccounts = (): Promise<null> => {
return apiPost('accounts/reinitialize');
};
Expand Down
23 changes: 23 additions & 0 deletions frontends/web/src/routes/settings/manage-accounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ class ManageAccounts extends Component<Props, State> {
this.toggleAccount(account.code, !active)
.then(() => event.target.disabled = false);
}} />
Watchonly:
<Toggle
checked={account.watch}
className={style.toggle}
id={account.code}
onChange={async (event) => {
event.target.disabled = true;
await this.setWatch(account.code, !account.watch);
event.target.disabled = false;
}} />
{active && account.coinCode === 'eth' ? (
<div className={style.tokenSection}>
<div className={`${style.tokenContainer} ${tokensVisible ? style.tokenContainerOpen : ''}`}>
Expand Down Expand Up @@ -131,6 +141,19 @@ class ManageAccounts extends Component<Props, State> {
});
};

private setWatch = async (accountCode: string, watch: boolean) => {
// TODO: ask user if they really want to proceed if they disable watch-only, if its keystore is
// not currently loaded. Disabling watch-only in this case immediately removes the account from
// the sidebar and manage accounts and cannot be brought back without connecting the keystore.

const result = await backendAPI.accountSetWatch(accountCode, watch);
if (result.success) {
await this.fetchAccounts();
} else if (result.errorMessage) {
alertUser(result.errorMessage);
}
};

private toggleShowTokens = (accountCode: string) => {
this.setState(({ showTokens }) => ({
showTokens: {
Expand Down

0 comments on commit 3e0cef0

Please sign in to comment.