Skip to content

Commit bfcbfeb

Browse files
committed
frontend/manage-accounts: add watchonly toggle per account
1 parent 94e1a7c commit bfcbfeb

File tree

5 files changed

+92
-0
lines changed

5 files changed

+92
-0
lines changed

backend/accounts.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,46 @@ func (backend *Backend) RenameAccount(accountCode accountsTypes.Code, name strin
487487
return nil
488488
}
489489

490+
// AccountSetWatch sets the account's persisted watch flag to `watch`. Set to `true` if the account
491+
// should be loaded even if its keystore is not connected.
492+
// If `watch` is set to `false`, the account is unloaded and the frontend notified.
493+
func (backend *Backend) AccountSetWatch(accountCode accountsTypes.Code, watch bool) error {
494+
err := backend.config.ModifyAccountsConfig(func(accountsConfig *config.AccountsConfig) error {
495+
acct := accountsConfig.Lookup(accountCode)
496+
if acct == nil {
497+
return errp.Newf("Could not find account %s", accountCode)
498+
}
499+
acct.Watch = &watch
500+
return nil
501+
})
502+
if err != nil {
503+
return err
504+
}
505+
defer backend.accountsAndKeystoreLock.Lock()()
506+
507+
// ETH tokens inherit the Watch-flag from the parent ETH account.
508+
// If the watch status of an ETH account was changed, we update it for its tokens as well.
509+
//
510+
// This ensures that removing a keystore after setting an ETH account including tokens to
511+
// watchonly results in the account *and* the tokens remaining loaded.
512+
if acct := backend.accounts.lookup(accountCode); acct != nil {
513+
if acct.Config().Config.CoinCode == coinpkg.CodeETH {
514+
for _, erc20TokenCode := range acct.Config().Config.ActiveTokens {
515+
erc20AccountCode := Erc20AccountCode(accountCode, erc20TokenCode)
516+
if tokenAcct := backend.accounts.lookup(erc20AccountCode); tokenAcct != nil {
517+
tokenAcct.Config().Config.Watch = &watch
518+
}
519+
}
520+
}
521+
}
522+
523+
if !watch {
524+
backend.initAccounts(false)
525+
backend.emitAccountsStatusChanged()
526+
}
527+
return nil
528+
}
529+
490530
// addAccount adds the given account to the backend.
491531
// The accountsAndKeystoreLock must be held when calling this function.
492532
func (backend *Backend) addAccount(account accounts.Interface) {

backend/handlers/handlers.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ type Backend interface {
9696
SetAccountActive(accountCode accountsTypes.Code, active bool) error
9797
SetTokenActive(accountCode accountsTypes.Code, tokenCode string, active bool) error
9898
RenameAccount(accountCode accountsTypes.Code, name string) error
99+
AccountSetWatch(accountCode accountsTypes.Code, watch bool) error
99100
AOPP() backend.AOPP
100101
AOPPCancel()
101102
AOPPApprove()
@@ -202,6 +203,7 @@ func NewHandlers(
202203
getAPIRouterNoError(apiRouter)("/set-account-active", handlers.postSetAccountActiveHandler).Methods("POST")
203204
getAPIRouterNoError(apiRouter)("/set-token-active", handlers.postSetTokenActiveHandler).Methods("POST")
204205
getAPIRouterNoError(apiRouter)("/rename-account", handlers.postRenameAccountHandler).Methods("POST")
206+
getAPIRouterNoError(apiRouter)("/account-set-watch", handlers.postAccountSetWatchHandler).Methods("POST")
205207
getAPIRouterNoError(apiRouter)("/accounts/reinitialize", handlers.postAccountsReinitializeHandler).Methods("POST")
206208
getAPIRouter(apiRouter)("/account-summary", handlers.getAccountSummary).Methods("GET")
207209
getAPIRouterNoError(apiRouter)("/supported-coins", handlers.getSupportedCoinsHandler).Methods("GET")
@@ -346,6 +348,7 @@ type accountJSON struct {
346348
// keystore.
347349
Keystore config.Keystore `json:"keystore"`
348350
Active bool `json:"active"`
351+
Watch bool `json:"watch"`
349352
CoinCode coinpkg.Code `json:"coinCode"`
350353
CoinUnit string `json:"coinUnit"`
351354
CoinName string `json:"coinName"`
@@ -362,6 +365,7 @@ func newAccountJSON(keystore config.Keystore, account accounts.Interface, active
362365
return &accountJSON{
363366
Keystore: keystore,
364367
Active: !account.Config().Config.Inactive,
368+
Watch: account.Config().Config.IsWatch(),
365369
CoinCode: account.Coin().Code(),
366370
CoinUnit: account.Coin().Unit(false),
367371
CoinName: account.Coin().Name(),
@@ -709,6 +713,26 @@ func (handlers *Handlers) postRenameAccountHandler(r *http.Request) interface{}
709713
return response{Success: true}
710714
}
711715

716+
func (handlers *Handlers) postAccountSetWatchHandler(r *http.Request) interface{} {
717+
var jsonBody struct {
718+
AccountCode accountsTypes.Code `json:"accountCode"`
719+
Watch bool `json:"watch"`
720+
}
721+
722+
type response struct {
723+
Success bool `json:"success"`
724+
ErrorMessage string `json:"errorMessage,omitempty"`
725+
}
726+
727+
if err := json.NewDecoder(r.Body).Decode(&jsonBody); err != nil {
728+
return response{Success: false, ErrorMessage: err.Error()}
729+
}
730+
if err := handlers.backend.AccountSetWatch(jsonBody.AccountCode, jsonBody.Watch); err != nil {
731+
return response{Success: false, ErrorMessage: err.Error()}
732+
}
733+
return response{Success: true}
734+
}
735+
712736
func (handlers *Handlers) postAccountsReinitializeHandler(_ *http.Request) interface{} {
713737
handlers.backend.ReinitializeAccounts()
714738
return nil

frontends/web/src/api/account.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type TKeystore = {
4848
export interface IAccount {
4949
keystore: TKeystore;
5050
active: boolean;
51+
watch: boolean;
5152
coinCode: CoinCode;
5253
coinUnit: string;
5354
coinName: string;

frontends/web/src/api/backend.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export const renameAccount = (accountCode: AccountCode, name: string): Promise<I
4747
return apiPost('rename-account', { accountCode, name });
4848
};
4949

50+
export const accountSetWatch = (accountCode: AccountCode, watch: boolean): Promise<ISuccess> => {
51+
return apiPost('account-set-watch', { accountCode, watch });
52+
};
53+
5054
export const reinitializeAccounts = (): Promise<null> => {
5155
return apiPost('accounts/reinitialize');
5256
};

frontends/web/src/routes/settings/manage-accounts.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ class ManageAccounts extends Component<Props, State> {
101101
this.toggleAccount(account.code, !active)
102102
.then(() => event.target.disabled = false);
103103
}} />
104+
Watchonly:
105+
<Toggle
106+
checked={account.watch}
107+
className={style.toggle}
108+
id={account.code}
109+
onChange={async (event) => {
110+
event.target.disabled = true;
111+
await this.setWatch(account.code, !account.watch);
112+
event.target.disabled = false;
113+
}} />
104114
{active && account.coinCode === 'eth' ? (
105115
<div className={style.tokenSection}>
106116
<div className={`${style.tokenContainer} ${tokensVisible ? style.tokenContainerOpen : ''}`}>
@@ -131,6 +141,19 @@ class ManageAccounts extends Component<Props, State> {
131141
});
132142
};
133143

144+
private setWatch = async (accountCode: string, watch: boolean) => {
145+
// TODO: ask user if they really want to proceed if they disable watch-only, if its keystore is
146+
// not currently loaded. Disabling watch-only in this case immediately removes the account from
147+
// the sidebar and manage accounts and cannot be brought back without connecting the keystore.
148+
149+
const result = await backendAPI.accountSetWatch(accountCode, watch);
150+
if (result.success) {
151+
await this.fetchAccounts();
152+
} else if (result.errorMessage) {
153+
alertUser(result.errorMessage);
154+
}
155+
};
156+
134157
private toggleShowTokens = (accountCode: string) => {
135158
this.setState(({ showTokens }) => ({
136159
showTokens: {

0 commit comments

Comments
 (0)