Added support for on-the-fly textview contents updates Added iterative view removal to ConsoleController Added dumy layouts to Common Added support for account removal Fixed command management (now supports leading, padding and trailing spaces) Various smaller changes
613 lines
27 KiB
C#
613 lines
27 KiB
C#
using Client.ConsoleForms;
|
|
using Client.ConsoleForms.Events;
|
|
using Client.ConsoleForms.Graphics;
|
|
using Client.ConsoleForms.Parameters;
|
|
using Client.Properties;
|
|
using ConsoleForms;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Tofvesson.Collections;
|
|
using Tofvesson.Common;
|
|
using Tofvesson.Crypto;
|
|
|
|
namespace Client
|
|
{
|
|
public sealed class SessionContext : Context
|
|
{
|
|
private readonly BankNetInteractor interactor;
|
|
private bool scheduleDestroy;
|
|
private Promise userDataGetter;
|
|
private Promise accountsGetter;
|
|
private List<string> accounts = null;
|
|
private string username;
|
|
private bool isAdministrator = false;
|
|
|
|
// Transient data
|
|
private int accountType = -1;
|
|
private string acc1 = null, acc2 = null, user = null;
|
|
|
|
// Stores personal accounts
|
|
private readonly FixedQueue<Tuple<string, Account>> accountDataCache = new FixedQueue<Tuple<string, Account>>(64);
|
|
|
|
// Stores remote account data
|
|
private readonly FixedQueue<Tuple<string, string>> remoteUserCache = new FixedQueue<Tuple<string, string>>(8);
|
|
private bool accountChange = false;
|
|
|
|
// XML-generated views
|
|
private ListView options;
|
|
private ButtonView options_exit;
|
|
private ButtonView options_view;
|
|
private ButtonView options_delete;
|
|
private ButtonView options_tx;
|
|
private ButtonView options_update;
|
|
private ButtonView options_add;
|
|
private InputView password_update;
|
|
private InputView transfer;
|
|
private DialogView exit_prompt;
|
|
private DialogView account_delete;
|
|
private InputView account_create;
|
|
|
|
// Synthetic
|
|
private ListView accountTypes;
|
|
|
|
// Deprecated
|
|
private DialogView success;
|
|
|
|
public SessionContext(ContextManager manager, BankNetInteractor interactor) : base(manager, "Session", "Common")
|
|
{
|
|
this.interactor = interactor;
|
|
scheduleDestroy = !interactor.IsLoggedIn;
|
|
|
|
if (!scheduleDestroy)
|
|
{
|
|
RegisterAutoHide("account_create", "account_info", "password_update", "exit_prompt", "account_show", "transfer");
|
|
|
|
|
|
// XML-generated views
|
|
options = GetView<ListView>("menu_options");
|
|
options_exit = options.GetView<ButtonView>("exit");
|
|
options_view = options.GetView<ButtonView>("view");
|
|
options_delete = options.GetView<ButtonView>("delete");
|
|
options_tx = options.GetView<ButtonView>("tx");
|
|
options_update = options.GetView<ButtonView>("update");
|
|
options_add = options.GetView<ButtonView>("add");
|
|
|
|
exit_prompt = GetView<DialogView>("exit_prompt");
|
|
password_update = GetView<InputView>("password_update");
|
|
transfer = GetView<InputView>("transfer");
|
|
account_delete = GetView<DialogView>("account_delete");
|
|
account_create = GetView<InputView>("account_create");
|
|
success = GetView<DialogView>("Success");
|
|
|
|
|
|
// Synthetic views
|
|
accountTypes = GenerateList(
|
|
new string[] {
|
|
GetIntlString("SE_acc_checking"),
|
|
GetIntlString("SE_acc_saving")
|
|
}, v =>
|
|
{
|
|
accountType = accountTypes.SelectedView;
|
|
account_create.Inputs[1].Text = (v as ButtonView).Text;
|
|
CreateAccount();
|
|
}, true);
|
|
|
|
// Run setup
|
|
SetupBackEvents();
|
|
SetupHideEvents();
|
|
SetupInputEvents();
|
|
SetupSubmissionEvents();
|
|
SetupDefaultViewStates();
|
|
|
|
// We have a valid context!
|
|
RefreshUserInfo(); // Get user info
|
|
RefreshAccountList(); // Get account list for user
|
|
}
|
|
}
|
|
|
|
private void SetupInputEvents()
|
|
{
|
|
transfer.InputListener = (v, i, s, t) =>
|
|
{
|
|
if (t) return false; // Don't handle artificial events
|
|
i.BackgroundColor = v.DefaultBackgroundColor;
|
|
i.SelectBackgroundColor = v.DefaultSelectBackgroundColor;
|
|
if (v.IndexOf(i) < 3)
|
|
{
|
|
// Trigger a keypress event for key [ENTER]
|
|
v.TriggerKeyEvent(new ConsoleController.KeyEvent(new ConsoleKeyInfo('\n', ConsoleKey.Enter, false, false, false)));
|
|
return false; // Don't update input
|
|
}
|
|
return true;
|
|
};
|
|
|
|
account_create.InputListener = (v, c, i, t) =>
|
|
{
|
|
c.BackgroundColor = v.DefaultBackgroundColor;
|
|
c.SelectBackgroundColor = v.DefaultSelectBackgroundColor;
|
|
if (v.IndexOf(c) == 1)
|
|
{
|
|
Show(accountTypes);
|
|
return false; // Don't process key event
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Set up a listener to reset color scheme
|
|
password_update.InputListener = (v, c, i, t) =>
|
|
{
|
|
c.BackgroundColor = v.DefaultBackgroundColor;
|
|
c.SelectBackgroundColor = v.DefaultSelectBackgroundColor;
|
|
return true;
|
|
};
|
|
}
|
|
|
|
private void ResetInputView(View v) => ResetInputView(v as InputView);
|
|
private void ResetInputView(InputView i)
|
|
{
|
|
i.SelectedField = 0;
|
|
foreach (var inputField in i.Inputs)
|
|
{
|
|
inputField.Text = "";
|
|
inputField.BackgroundColor = i.DefaultBackgroundColor;
|
|
inputField.SelectBackgroundColor = i.DefaultSelectBackgroundColor;
|
|
inputField.SelectIndex = 0;
|
|
inputField.RenderStart = 0;
|
|
}
|
|
}
|
|
|
|
private void ResetDialogView(View d) => ResetDialogView(d as DialogView);
|
|
private void ResetDialogView(DialogView d) => d.Select = 0;
|
|
|
|
private void ResetListView(View d) => ResetListView(d as ListView);
|
|
private void ResetListView(ListView d) => d.SelectedView = 0;
|
|
|
|
private void SetupHideEvents()
|
|
{
|
|
password_update.OnClose = ResetInputView;
|
|
transfer.OnClose = _ =>
|
|
{
|
|
ResetInputView(transfer);
|
|
transfer.Inputs[0].Text = GetIntlString("SE_account_select");
|
|
transfer.Inputs[1].Text = GetIntlString("SE_user_select");
|
|
transfer.Inputs[2].Text = GetIntlString("SE_account_select");
|
|
acc1 = null;
|
|
acc2 = null;
|
|
user = null;
|
|
};
|
|
options.OnClose = ResetListView;
|
|
account_delete.OnClose = ResetDialogView;
|
|
account_create.OnClose = _ =>
|
|
{
|
|
ResetInputView(account_create);
|
|
account_create.Inputs[1].Text = GetIntlString("SE_acc_sel");
|
|
accountType = -1;
|
|
};
|
|
exit_prompt.OnClose = ResetDialogView;
|
|
}
|
|
|
|
private void SetupBackEvents()
|
|
{
|
|
options.OnBackEvent = v => Show("exit_prompt");
|
|
}
|
|
|
|
private void SetupDefaultViewStates()
|
|
{
|
|
account_create.OnClose(account_create);
|
|
transfer.OnClose(transfer);
|
|
}
|
|
|
|
private void SetupSubmissionEvents()
|
|
{
|
|
// Setup options menu events
|
|
options_add.SetEvent(_ => Show(account_create));
|
|
options_update.SetEvent(v => Show(password_update));
|
|
options_tx.SetEvent(v => Show(transfer));
|
|
options_delete.SetEvent(v => Show(account_delete));
|
|
|
|
// Other events
|
|
account_delete.RegisterSelectListener((v, i, s) =>
|
|
{
|
|
Hide(v);
|
|
if (i == 1)
|
|
{
|
|
Show("delete_stall");
|
|
Promise deletion = Promise.AwaitPromise(interactor.DeleteUser());
|
|
deletion.Subscribe = p =>
|
|
{
|
|
Hide("delete_stall");
|
|
if (bool.Parse(p.Value))
|
|
controller.Popup(GetIntlString("SE_delete_success"), 2500, ConsoleColor.Green, () => manager.LoadContext(new WelcomeContext(manager, interactor)));
|
|
else
|
|
controller.Popup(GetIntlString("SE_delete_failure"), 1500, ConsoleColor.Red);
|
|
};
|
|
}
|
|
});
|
|
|
|
account_create.SubmissionsListener = inputView =>
|
|
{
|
|
if (inputView.SelectedField == 1)
|
|
Show(accountTypes); // Show account type selection menu
|
|
else CreateAccount();
|
|
|
|
};
|
|
|
|
success.RegisterSelectListener((v, i, s) => HandleLogout());
|
|
|
|
options_exit.SetEvent(v => Show("exit_prompt"));
|
|
|
|
options_view.SetEvent(v =>
|
|
{
|
|
if (accountChange) RefreshAccountList();
|
|
if (!accountsGetter.HasValue) Show("data_fetch");
|
|
accountsGetter.Subscribe = p =>
|
|
{
|
|
accountsGetter.Unsubscribe();
|
|
Hide("data_fetch");
|
|
|
|
Show(GenerateList(p.Value.Split('&').ForEach(Support.FromBase64String), ViewAccountListener));
|
|
};
|
|
});
|
|
|
|
password_update.SubmissionsListener = v =>
|
|
{
|
|
bool hasError = v.Inputs[0].Text.Length == 0;
|
|
if (hasError)
|
|
{
|
|
// Notify user, as well as mark the errant input field
|
|
v.Inputs[0].SelectBackgroundColor = ConsoleColor.Red;
|
|
v.Inputs[0].BackgroundColor = ConsoleColor.DarkRed;
|
|
controller.Popup(GetIntlString("ERR_empty"), 3000, ConsoleColor.Red);
|
|
}
|
|
if (v.Inputs[1].Text.Length == 0)
|
|
{
|
|
v.Inputs[1].SelectBackgroundColor = ConsoleColor.Red;
|
|
v.Inputs[1].BackgroundColor = ConsoleColor.DarkRed;
|
|
if (!hasError) controller.Popup(GetIntlString("ERR_empty"), 3000, ConsoleColor.Red);
|
|
return; // No need to continue, we have notified the user. There is no valid information to operate on past this point
|
|
}
|
|
if (!v.Inputs[0].Text.Equals(v.Inputs[1].Text))
|
|
{
|
|
controller.Popup(GetIntlString("SU_mismatch"), 3000, ConsoleColor.Red);
|
|
return;
|
|
}
|
|
Show("update_stall");
|
|
Task<Promise> t = interactor.UpdatePassword(v.Inputs[0].Text);
|
|
Promise.AwaitPromise(t).Subscribe = p =>
|
|
{
|
|
Hide("update_stall");
|
|
Hide("password_update");
|
|
v.Inputs[0].ClearText();
|
|
v.Inputs[1].ClearText();
|
|
v.SelectedField = 0;
|
|
};
|
|
};
|
|
|
|
transfer.SubmissionsListener = v =>
|
|
{
|
|
switch (v.SelectedField)
|
|
{
|
|
case 0:
|
|
if (accountChange) accountsGetter = Promise.AwaitPromise(interactor.ListUserAccounts());
|
|
Show("data_fetch");
|
|
accountsGetter.Subscribe = p =>
|
|
{
|
|
accountsGetter.Unsubscribe();
|
|
Hide("data_fetch");
|
|
|
|
Show(GenerateList(p.Value.Split('&').ForEach(Support.FromBase64String), sel => v.Inputs[0].Text = acc1 = (sel as ButtonView).Text, true));
|
|
};
|
|
break;
|
|
case 1:
|
|
Show("data_fetch");
|
|
Promise remoteUserGetter = Promise.AwaitPromise(interactor.ListUsers());
|
|
remoteUserGetter.Subscribe = p =>
|
|
{
|
|
remoteUserGetter.Unsubscribe();
|
|
Hide("data_fetch");
|
|
|
|
Show(GenerateList(p.Value.Split('&').ForEach(Support.FromBase64String), sel => v.Inputs[1].Text = user = (sel as ButtonView).Text, true));
|
|
};
|
|
break;
|
|
case 2:
|
|
if (user == null)
|
|
controller.Popup(GetIntlString("SE_user_noselect"), 2000, ConsoleColor.Red);
|
|
else
|
|
{
|
|
Show("data_fetch");
|
|
Promise remoteAccountsGetter = Promise.AwaitPromise(interactor.ListAccounts(user));
|
|
remoteAccountsGetter.Subscribe = p =>
|
|
{
|
|
remoteAccountsGetter.Unsubscribe();
|
|
Hide("data_fetch");
|
|
|
|
Show(GenerateList(p.Value.Split('&').ForEach(Support.FromBase64String), sel => v.Inputs[2].Text = acc2 = (sel as ButtonView).Text, true));
|
|
};
|
|
}
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
Show("verify_stall");
|
|
bool error = false;
|
|
if (acc1 == null)
|
|
{
|
|
controller.Popup(GetIntlString("SE_account_noselect"), 1500, ConsoleColor.Red);
|
|
error = true;
|
|
v.Inputs[0].BackgroundColor = ConsoleColor.Red;
|
|
v.Inputs[0].SelectBackgroundColor = ConsoleColor.DarkRed;
|
|
}
|
|
if (acc2 == null)
|
|
{
|
|
if (!error) controller.Popup(GetIntlString("SE_account_noselect"), 1500, ConsoleColor.Red);
|
|
error = true;
|
|
v.Inputs[2].BackgroundColor = ConsoleColor.Red;
|
|
v.Inputs[2].SelectBackgroundColor = ConsoleColor.DarkRed;
|
|
}
|
|
if (user == null)
|
|
{
|
|
if (!error) controller.Popup(GetIntlString("SE_account_nouser"), 1500, ConsoleColor.Red);
|
|
error = true;
|
|
v.Inputs[1].BackgroundColor = ConsoleColor.DarkRed;
|
|
v.Inputs[1].SelectBackgroundColor = ConsoleColor.Red;
|
|
}
|
|
userDataGetter = Promise.AwaitPromise(interactor.UserInfo());
|
|
userDataGetter.Subscribe = p =>
|
|
{
|
|
userDataGetter.Unsubscribe();
|
|
var account = AccountLookup("SE_balance_toohigh");
|
|
if (account == null) accountsGetter = Promise.AwaitPromise(interactor.AccountInfo(acc1));
|
|
accountsGetter.Subscribe = result =>
|
|
{
|
|
accountsGetter.Unsubscribe();
|
|
var resultData = p.Value.Split('&');
|
|
Hide("verify_stall");
|
|
decimal d;
|
|
if (result.Value.StartsWith("ERROR") || !Account.TryParse(result.Value, out var act))
|
|
controller.Popup(GetIntlString("GENERIC_error"), 1500, ConsoleColor.Red);
|
|
else if ((d = decimal.Parse(v.Inputs[3].Text)) > act.balance && (!bool.Parse(resultData[1]) || !acc1.Equals(acc2)))
|
|
controller.Popup(GetIntlString("SE_balance_toohigh").Replace("$0", act.balance.ToString()), 3000, ConsoleColor.Red);
|
|
else
|
|
{
|
|
Promise txPromise = Promise.AwaitPromise(interactor.CreateTransaction(acc1, user, acc2, d, v.Inputs[4].Text.Length == 0 ? null : v.Inputs[4].Text));
|
|
accountChange = true;
|
|
accountDataCache.Clear();
|
|
txPromise.Subscribe = txResult =>
|
|
{
|
|
if (txResult.Value.StartsWith("ERROR"))
|
|
controller.Popup(GetIntlString("GENERIC_error"), 1500, ConsoleColor.Red);
|
|
else controller.Popup(GetIntlString("SE_tx_success"), 2000, ConsoleColor.Green, () => Hide("transfer"));
|
|
};
|
|
}
|
|
};
|
|
};
|
|
break;
|
|
}
|
|
};
|
|
|
|
exit_prompt.RegisterSelectListener((v, i, s) =>
|
|
{
|
|
if (i == 0) Hide("exit_prompt");
|
|
else HandleLogout();
|
|
});
|
|
}
|
|
|
|
private ListView GenerateList(string[] data, SubmissionEvent onclick, bool exitOnSubmit = false, bool hideOnBack = true)
|
|
{
|
|
//var list = GetView<ListView>("account_show");
|
|
var list = new ListView(new ViewData("ListView").SetAttribute("padding_left", 2).SetAttribute("padding_right", 2).SetAttribute("border", 8), LangManager.NO_LANG);
|
|
//if (data.Length == 1 && data[0].Length == 0) data = new string[0];
|
|
Tuple<string, View>[] listData = new Tuple<string, View>[data.Length - ((data.Length == 1 && data[0].Length == 0) ? 1 : 0)];
|
|
for (int i = 0; i < listData.Length; ++i)
|
|
{
|
|
ButtonView t = new ButtonView(new ViewData("ButtonView").AddNestedSimple("Text", data[i]), LangManager.NO_LANG); // Don't do translations
|
|
t.SetEvent(v =>
|
|
{
|
|
onclick?.Invoke(v);
|
|
if (exitOnSubmit) Hide(list);
|
|
});
|
|
listData[i] = new Tuple<string, View>(t.Text, t);
|
|
}
|
|
if (listData.Length > 0) list.AddViews(0, listData);
|
|
else
|
|
{
|
|
ButtonView close = new ButtonView(new ViewData("ButtonView").AddNestedSimple("Text", GetIntlString("GENERIC_dismiss")), LangManager.NO_LANG);
|
|
close.SetEvent(_ => Hide(list));
|
|
list.AddView(close, "close");
|
|
}
|
|
if (hideOnBack) list.OnBackEvent = v => Hide(v);
|
|
return list;
|
|
}
|
|
|
|
private void RefreshAccountList()
|
|
{
|
|
accountsGetter = Promise.AwaitPromise(interactor.ListUserAccounts()); // Get accounts associated with this user
|
|
|
|
accountsGetter.Subscribe = p =>
|
|
{
|
|
var data = p.Value.Split('&');
|
|
accounts = new List<string>();
|
|
accounts.AddRange(data);
|
|
};
|
|
}
|
|
|
|
private void RefreshUserInfo()
|
|
{
|
|
userDataGetter = Promise.AwaitPromise(interactor.UserInfo()); // Get basic user info
|
|
|
|
userDataGetter.Subscribe = p =>
|
|
{
|
|
var data = p.Value.Split('&');
|
|
username = data[0].FromBase64String();
|
|
isAdministrator = bool.Parse(data[1]);
|
|
};
|
|
}
|
|
|
|
private void HandleLogout(bool automatic = false)
|
|
{
|
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
interactor.Logout();
|
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
controller.Popup(GetIntlString($"SE_{(automatic ? "auto" : "")}lo"), 2500, ConsoleColor.DarkMagenta, () => manager.LoadContext(new WelcomeContext(manager, interactor)));
|
|
}
|
|
|
|
private Tuple<string, Account> AccountLookup(string name)
|
|
{
|
|
foreach (var cacheEntry in accountDataCache)
|
|
if (cacheEntry.Item1.Equals(name))
|
|
return cacheEntry;
|
|
return null;
|
|
}
|
|
|
|
public override void OnCreate()
|
|
{
|
|
if (scheduleDestroy) manager.LoadContext(new WelcomeContext(manager, interactor));
|
|
else Show("menu_options");
|
|
}
|
|
|
|
public override void OnDestroy()
|
|
{
|
|
base.HideAll();
|
|
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
interactor.CancelAll();
|
|
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
}
|
|
|
|
|
|
|
|
void CreateAccount()
|
|
{
|
|
bool hasError = false;
|
|
if (accountType == -1)
|
|
{
|
|
hasError = true;
|
|
account_create.Inputs[1].SelectBackgroundColor = ConsoleColor.Red;
|
|
account_create.Inputs[1].BackgroundColor = ConsoleColor.DarkRed;
|
|
controller.Popup(GetIntlString("SE_acc_nosel"), 2500, ConsoleColor.Red);
|
|
}
|
|
if (account_create.Inputs[0].Text.Length == 0)
|
|
{
|
|
account_create.Inputs[0].SelectBackgroundColor = ConsoleColor.Red;
|
|
account_create.Inputs[0].BackgroundColor = ConsoleColor.DarkRed;
|
|
if (!hasError) controller.Popup(GetIntlString("ERR_empty"), 3000, ConsoleColor.Red);
|
|
}
|
|
else if(!hasError)
|
|
{
|
|
void AlreadyExists()
|
|
=> controller.Popup(GetIntlString("SE_account_exists").Replace("$0", account_create.Inputs[0].Text), 2500, ConsoleColor.Red, () => Hide(account_create));
|
|
|
|
var act = AccountLookup(account_create.Inputs[0].Text);
|
|
if (act != null) AlreadyExists();
|
|
else
|
|
{
|
|
Show("account_stall");
|
|
Promise accountPromise = Promise.AwaitPromise(interactor.CreateAccount(account_create.Inputs[0].Text, accountType == 0));
|
|
accountPromise.Subscribe = p =>
|
|
{
|
|
if (bool.Parse(p.Value))
|
|
{
|
|
controller.Popup(GetIntlString("SE_account_success"), 750, ConsoleColor.Green, () => Hide(account_create));
|
|
accountChange = true;
|
|
}
|
|
else AlreadyExists();
|
|
Hide("account_stall");
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ViewAccountListener(View listener)
|
|
{
|
|
ButtonView view = listener as ButtonView;
|
|
|
|
void ShowAccountData(string name, decimal balance, Account.AccountType type)
|
|
{
|
|
// Build dialog view manually
|
|
var show = new DialogView(
|
|
new ViewData("DialogView")
|
|
|
|
// Layout parameters
|
|
.SetAttribute("padding_left", 2)
|
|
.SetAttribute("padding_right", 2)
|
|
.SetAttribute("padding_top", 1)
|
|
.SetAttribute("padding_bottom", 1)
|
|
.SetAttribute("border", (int)ConsoleColor.DarkGreen)
|
|
|
|
// Option buttons
|
|
.AddNested(new ViewData("Options").AddNestedSimple("Option", GetIntlString("GENERIC_dismiss")).AddNestedSimple("Option", GetIntlString("SE_account_delete")))
|
|
|
|
// Message
|
|
.AddNestedSimple("Text",
|
|
GetIntlString("SE_info")
|
|
.Replace("$0", name)
|
|
.Replace("$1", GetIntlString(type == Account.AccountType.Savings ? "SE_acc_saving" : "SE_acc_checking"))
|
|
.Replace("$2", balance.ToTruncatedString())),
|
|
|
|
// No translation (it's already handled)
|
|
LangManager.NO_LANG);
|
|
|
|
show.RegisterSelectListener((_, s, l) =>
|
|
{
|
|
if(s==0) Hide(show);
|
|
else
|
|
{
|
|
var ynDialog = GetView<DialogView>("yn");
|
|
ynDialog.Text = GetIntlString("SE_account_delete_warn");
|
|
ynDialog.RegisterSelectListener((v, i, str) =>
|
|
{
|
|
var stall = GetView<TextView>("stall");
|
|
stall.Text = GetIntlString("SE_account_delete_stall");
|
|
Show(stall);
|
|
if (i == 1)
|
|
{
|
|
Promise p = Promise.AwaitPromise(interactor.CloseAccount(name));
|
|
p.Subscribe = deleteAwait =>
|
|
{
|
|
if (bool.Parse(deleteAwait.Value))
|
|
{
|
|
accountChange = true;
|
|
controller.Popup(GetIntlString("SE_account_delete_success"), 1500, ConsoleColor.Green, () => {
|
|
bool closed = false;
|
|
controller.CloseIf(predV => closed = !closed && predV is ListView);
|
|
Hide(show);
|
|
});
|
|
}
|
|
else controller.Popup(GetIntlString("SE_account_delete_fail"), 2000, ConsoleColor.Red);
|
|
Hide(stall);
|
|
};
|
|
}
|
|
});
|
|
Show(ynDialog);
|
|
}
|
|
});
|
|
Show(show);
|
|
}
|
|
|
|
// TODO: Show account info
|
|
var account = AccountLookup(view.Text);
|
|
if (account == null)
|
|
{
|
|
// TODO: Get account data from server + cache data
|
|
Show("data_fetch");
|
|
Promise info_promise = Promise.AwaitPromise(interactor.AccountInfo(view.Text));
|
|
info_promise.Subscribe = evt =>
|
|
{
|
|
Hide("data_fetch");
|
|
if (evt.Value.StartsWith("ERROR") || !Account.TryParse(evt.Value, out var act))
|
|
controller.Popup(GetIntlString("GENERIC_error"), 3000, ConsoleColor.Red);
|
|
else
|
|
{
|
|
// Cache result (don't cache savings accounts because their value updates pretty frequently)
|
|
if (act.type != Account.AccountType.Savings) accountDataCache.Enqueue(new Tuple<string, Account>(view.Text, act));
|
|
ShowAccountData(view.Text, act.balance, act.type);
|
|
}
|
|
|
|
};
|
|
}
|
|
else ShowAccountData(account.Item1, account.Item2.balance, account.Item2.type);
|
|
}
|
|
}
|
|
}
|