First-run wallet creatorator (v0.7.0) (#24)

Adds a first-run wallet and account creator option for noobs.
If they select the "create a default wallet" option, they are jumped to the name + password screen for wallet creation, and a new account is generated for them named "Account 1".

This leapfrogs the problem of users having to know what is going on with the blockchain and wallet at all before getting started.
#18
#19

Reviewed-on: #24
Reviewed-by: Ulf Wiger <ulfwiger@qpq.swiss>
Co-authored-by: Craig Everett <zxq9@zxq9.com>
Co-committed-by: Craig Everett <zxq9@zxq9.com>
This commit is contained in:
Craig Everett 2025-08-07 15:47:05 +09:00 committed by Craig Everett
parent a2a9da0d98
commit f92c5fbde0
13 changed files with 236 additions and 80 deletions

View File

@ -3,7 +3,7 @@
{registered,[]}, {registered,[]},
{included_applications,[]}, {included_applications,[]},
{applications,[stdlib,kernel,sasl,ssl]}, {applications,[stdlib,kernel,sasl,ssl]},
{vsn,"0.6.6"}, {vsn,"0.7.0"},
{modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt, {modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt,
gd_sophia_editor,gd_sup,gd_v,gd_v_devman,gd_v_netman, gd_sophia_editor,gd_sup,gd_v,gd_v_devman,gd_v_netman,
gd_v_wallman]}, gd_v_wallman]},

View File

@ -3,7 +3,7 @@
%%% @end %%% @end
-module(gajudesk). -module(gajudesk).
-vsn("0.6.6"). -vsn("0.7.0").
-behavior(application). -behavior(application).
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").

View File

@ -3,7 +3,7 @@
%%% @end %%% @end
-module(gd_con). -module(gd_con).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").
@ -63,15 +63,16 @@
when Name :: ui_name(). when Name :: ui_name().
show_ui(Name) -> show_ui(Name) ->
gen_server:cast(?MODULE, {show_ui, Name}). gen_server:call(?MODULE, {show_ui, Name}).
-spec open_wallet(Path, Phrase) -> ok -spec open_wallet(Path, Phrase) -> Result
when Path :: file:filename(), when Path :: file:filename(),
Phrase :: string(). Phrase :: string(),
Result :: ok | {error, Reason :: term()}.
open_wallet(Path, Phrase) -> open_wallet(Path, Phrase) ->
gen_server:cast(?MODULE, {open_wallet, Path, Phrase}). gen_server:call(?MODULE, {open_wallet, Path, Phrase}).
-spec close_wallet() -> ok. -spec close_wallet() -> ok.
@ -217,7 +218,7 @@ deploy(Build, Params, InitArgs) ->
Size :: 256, Size :: 256,
Name :: string(), Name :: string(),
Seed :: string(), Seed :: string(),
Encoding :: utf8 | base64 | base58, Encoding :: utf8 | base64 | base58 | none,
Transform :: {Algo, Yugeness}, Transform :: {Algo, Yugeness},
Algo :: sha3 | sha2 | x_or | pbkdf2, Algo :: sha3 | sha2 | x_or | pbkdf2,
Yugeness :: rand | non_neg_integer(). Yugeness :: rand | non_neg_integer().
@ -328,6 +329,7 @@ init(none) ->
T = erlang:send_after(tic(), self(), tic), T = erlang:send_after(tic(), self(), tic),
State = #s{window = Window, timer = T, wallets = Wallets, prefs = Prefs}, State = #s{window = Window, timer = T, wallets = Wallets, prefs = Prefs},
NewState = do_show_ui(gd_v_wallman, State), NewState = do_show_ui(gd_v_wallman, State),
ok = gd_v_wallman:first_run(),
{ok, NewState}. {ok, NewState}.
@ -365,6 +367,9 @@ handle_call(list_keys, _, State) ->
handle_call({nonce, ID}, _, State) -> handle_call({nonce, ID}, _, State) ->
Response = do_nonce(ID), Response = do_nonce(ID),
{reply, Response, State}; {reply, Response, State};
handle_call({open_wallet, Path, Phrase}, _, State) ->
{Response, NewState} = do_open_wallet(Path, Phrase, State),
{reply, Response, NewState};
handle_call(network, _, State) -> handle_call(network, _, State) ->
Response = do_network(State), Response = do_network(State),
{reply, Response, State}; {reply, Response, State};
@ -374,6 +379,9 @@ handle_call({save, Module, Prefs}, _, State) ->
handle_call({mnemonic, ID}, _, State) -> handle_call({mnemonic, ID}, _, State) ->
Response = do_mnemonic(ID, State), Response = do_mnemonic(ID, State),
{reply, Response, State}; {reply, Response, State};
handle_call({show_ui, Name}, _, State) ->
NewState = do_show_ui(Name, State),
{reply, ok, NewState};
handle_call(Unexpected, From, State) -> handle_call(Unexpected, From, State) ->
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
{noreply, State}. {noreply, State}.
@ -387,16 +395,11 @@ handle_call(Unexpected, From, State) ->
%% The gen_server:handle_cast/2 callback. %% The gen_server:handle_cast/2 callback.
%% See: http://erlang.org/doc/man/gen_server.html#Module:handle_cast-2 %% See: http://erlang.org/doc/man/gen_server.html#Module:handle_cast-2
handle_cast({show_ui, Name}, State) ->
NewState = do_show_ui(Name, State),
{noreply, NewState};
handle_cast({open_wallet, Path, Phrase}, State) ->
NewState = do_open_wallet(Path, Phrase, State),
{noreply, NewState};
handle_cast(close_wallet, State) -> handle_cast(close_wallet, State) ->
NextState = do_close_wallet(State), NextState = do_close_wallet(State),
ok = gd_gui:show([]), ok = gd_gui:show([]),
NewState = do_show_ui(gd_v_wallman, NextState), NewState = do_show_ui(gd_v_wallman, NextState),
ok = gd_v_wallman:to_front(),
{noreply, NewState}; {noreply, NewState};
handle_cast({new_wallet, Net, Name, Path, Password}, State) -> handle_cast({new_wallet, Net, Name, Path, Password}, State) ->
NewState = do_new_wallet(Net, Name, Path, Password, State), NewState = do_new_wallet(Net, Name, Path, Password, State),
@ -477,6 +480,9 @@ handle_cast(Unexpected, State) ->
handle_info(tic, State) -> handle_info(tic, State) ->
NewState = do_tic(State), NewState = do_tic(State),
{noreply, NewState}; {noreply, NewState};
handle_info({show_ui, Name}, State) ->
NewState = do_show_ui(Name, State),
{noreply, NewState};
handle_info({'DOWN', Mon, process, PID, Info}, State) -> handle_info({'DOWN', Mon, process, PID, Info}, State) ->
NewState = handle_down(Mon, PID, Info, State), NewState = handle_down(Mon, PID, Info, State),
{noreply, NewState}; {noreply, NewState};
@ -531,7 +537,6 @@ do_show_ui(Name, State = #s{tasks = Tasks, prefs = Prefs}) ->
Win = Name:start_link({TaskPrefs, TaskData}), Win = Name:start_link({TaskPrefs, TaskData}),
PID = wx_object:get_pid(Win), PID = wx_object:get_pid(Win),
Mon = monitor(process, PID), Mon = monitor(process, PID),
ok = Name:to_front(Win),
UI = #ui{name = Name, pid = PID, wx = Win, mon = Mon}, UI = #ui{name = Name, pid = PID, wx = Win, mon = Mon},
State#s{tasks = [UI | Tasks]} State#s{tasks = [UI | Tasks]}
end. end.
@ -863,7 +868,9 @@ do_make_key(Name, Seed, base58, Transform, State) ->
do_make_key2(_, _, _, State = #s{wallet = none}) -> do_make_key2(_, _, _, State = #s{wallet = none}) ->
ok = gd_gui:trouble("No wallet selected!"), ok = gd_gui:trouble("No wallet selected!"),
do_show_ui(gd_v_wallman, State); NewState = do_show_ui(gd_v_wallman, State),
ok = gd_v_wallman:to_front(),
NewState;
do_make_key2(Name, Bin, Transform, do_make_key2(Name, Bin, Transform,
State = #s{wallet = Current, wallets = Wallets, pass = Pass}) -> State = #s{wallet = Current, wallets = Wallets, pass = Pass}) ->
#wallet{name = WalletName, poas = POAs, keys = Keys} = Current, #wallet{name = WalletName, poas = POAs, keys = Keys} = Current,
@ -1020,17 +1027,12 @@ do_open_wallet(Path, Phrase, State) ->
{ok, ChainID} -> gd_gui:chain(ChainID, Node); {ok, ChainID} -> gd_gui:chain(ChainID, Node);
Error -> gd_gui:trouble(Error) Error -> gd_gui:trouble(Error)
end, end,
State#s{pass = Pass, wallet = Recovered}; {ok, State#s{pass = Pass, wallet = Recovered}};
Error -> Error ->
ok = gd_gui:trouble(Error), {Error, State}
New = default_wallet(),
State#s{wallet = New}
end. end.
default_wallet() ->
default_wallet(testnet).
default_wallet(mainnet) -> default_wallet(mainnet) ->
Node = #node{ip = "groot.mainnet.gajumaru.io"}, Node = #node{ip = "groot.mainnet.gajumaru.io"},
Groot = #chain{id = <<"groot.mainnet">>, Groot = #chain{id = <<"groot.mainnet">>,

View File

@ -37,7 +37,7 @@
%%% @end %%% @end
-module(gd_grids). -module(gd_grids).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").
@ -59,7 +59,7 @@
Reason :: bad_url. Reason :: bad_url.
parse(URL) -> parse(URL) ->
case uri_string:parse(URL) of case uri_string:parse(string:trim(URL)) of
#{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grids"} -> #{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grids"} ->
spend(R, chain, list_to_binary(H), Q); spend(R, chain, list_to_binary(H), Q);
#{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grid"} -> #{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grid"} ->

View File

@ -3,7 +3,7 @@
%%% @end %%% @end
-module(gd_gui). -module(gd_gui).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").
@ -361,6 +361,7 @@ refresh(State) ->
wallman(State) -> wallman(State) ->
ok = gd_con:show_ui(gd_v_wallman), ok = gd_con:show_ui(gd_v_wallman),
ok = gd_v_wallman:to_front(),
State. State.

View File

@ -15,7 +15,7 @@
%%% translation library is retained). %%% translation library is retained).
-module(gd_jt). -module(gd_jt).
-vsn("0.6.6"). -vsn("0.7.0").
-export([read_translations/1, j/2, oneshot_j/2]). -export([read_translations/1, j/2, oneshot_j/2]).

View File

@ -1,5 +1,5 @@
-module(gd_sophia_editor). -module(gd_sophia_editor).
-vsn("0.6.6"). -vsn("0.7.0").
-export([new/1, update/2, -export([new/1, update/2,
get_text/1, set_text/2]). get_text/1, set_text/2]).

View File

@ -12,7 +12,7 @@
%%% @end %%% @end
-module(gd_sup). -module(gd_sup).
-vsn("0.6.6"). -vsn("0.7.0").
-behaviour(supervisor). -behaviour(supervisor).
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").

View File

@ -1,5 +1,5 @@
-module(gd_v). -module(gd_v).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").

View File

@ -1,5 +1,5 @@
-module(gd_v_devman). -module(gd_v_devman).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <craigeverett@qpq.swiss>"). -author("Craig Everett <craigeverett@qpq.swiss>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").

View File

@ -1,5 +1,5 @@
-module(gd_v_netman). -module(gd_v_netman).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <zxq9@zxq9.com>"). -author("Craig Everett <zxq9@zxq9.com>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").

View File

@ -1,5 +1,19 @@
%%% @doc
%%% The GajuDesk Wallet Manager
%%%
%%% This is an interface for managing multiple wallets.
%%% A wallet is defined as a collection of accounts/keys.
%%%
%%% NOTE:
%%% Any call to `gd_con:show_ui(gd_v_wallman)' must be followed by a call to
%%% either `gd_v_wallman:to_front()' or `gd_v_wallman:first_run()' or else
%%% this UI process will just sit in the background (invisible) until told to
%%% do something.
%%% @end
-module(gd_v_wallman). -module(gd_v_wallman).
-vsn("0.6.6"). -vsn("0.7.0").
-author("Craig Everett <zxq9@zxq9.com>"). -author("Craig Everett <zxq9@zxq9.com>").
-copyright("QPQ AG <info@qpq.swiss>"). -copyright("QPQ AG <info@qpq.swiss>").
-license("GPL-3.0-or-later"). -license("GPL-3.0-or-later").
@ -7,7 +21,7 @@
-behavior(wx_object). -behavior(wx_object).
%-behavior(gd_v). %-behavior(gd_v).
-include_lib("wx/include/wx.hrl"). -include_lib("wx/include/wx.hrl").
-export([to_front/1]). -export([to_front/0, to_front/1, first_run/0, trouble/1]).
-export([show/2]). -export([show/2]).
-export([start_link/1]). -export([start_link/1]).
-export([init/1, terminate/2, code_change/3, -export([init/1, terminate/2, code_change/3,
@ -29,11 +43,18 @@
prefs = #{} :: map(), prefs = #{} :: map(),
wallets = [] :: [#wr{}], wallets = [] :: [#wr{}],
picker = none :: none | wx:wx_object(), picker = none :: none | wx:wx_object(),
buttons = [] :: [#w{}]}). buttons = [] :: [#w{}],
wiz = none :: none | {wx:wx_object(), Buttons :: [#w{}]}}).
%%% Interface %%% Interface
-spec to_front() -> ok.
to_front() ->
wx_object:cast(?MODULE, to_front).
-spec to_front(Win) -> ok -spec to_front(Win) -> ok
when Win :: wx:wx_object(). when Win :: wx:wx_object().
@ -41,6 +62,19 @@ to_front(Win) ->
wx_object:cast(Win, to_front). wx_object:cast(Win, to_front).
-spec first_run() -> ok.
first_run() ->
wx_object:cast(?MODULE, first_run).
-spec trouble(Info) -> ok
when Info :: term().
trouble(Info) ->
wx_object:cast(?MODULE, {trouble, Info}).
-spec show(Win, Manifest) -> ok -spec show(Win, Manifest) -> ok
when Win :: wx:xw_object(), when Win :: wx:xw_object(),
Manifest :: [#wr{}]. Manifest :: [#wr{}].
@ -107,16 +141,7 @@ init({Prefs, Manifest}) ->
ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, command_button_clicked),
ok = wxFrame:connect(Frame, close_window), ok = wxFrame:connect(Frame, close_window),
true = wxFrame:show(Frame),
ok = wxListBox:connect(Picker, command_listbox_doubleclicked), ok = wxListBox:connect(Picker, command_listbox_doubleclicked),
ok =
case length(Manifest) =:= 0 of
false ->
ok;
true ->
self() ! new,
ok
end,
State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs, State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs,
wallets = Manifest, wallets = Manifest,
picker = Picker, picker = Picker,
@ -125,16 +150,23 @@ init({Prefs, Manifest}) ->
%%% wx_object %%% wx_object
handle_call(Unexpected, From, State) -> handle_call(Unexpected, From, State) ->
ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]),
{noreply, State}. {noreply, State}.
handle_cast(to_front, State = #s{frame = Frame}) -> handle_cast(to_front, State = #s{frame = Frame}) ->
ok = ensure_shown(Frame),
ok = wxFrame:raise(Frame), ok = wxFrame:raise(Frame),
{noreply, State}; {noreply, State};
handle_cast(first_run, State) ->
NewState = do_first_run(State),
{noreply, NewState};
handle_cast({trouble, Info}, State) ->
ok = handle_troubling(State, Info),
{noreply, State};
handle_cast({show, Manifest}, State) -> handle_cast({show, Manifest}, State) ->
NewState = do_show(Manifest, State), NewState = do_show(Manifest, State),
{noreply, NewState}; {noreply, NewState};
@ -143,9 +175,6 @@ handle_cast(Unexpected, State) ->
{noreply, State}. {noreply, State}.
handle_info(new, State) ->
NewState = do_new(State),
{noreply, NewState};
handle_info(Unexpected, State) -> handle_info(Unexpected, State) ->
ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]), ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]),
{noreply, State}. {noreply, State}.
@ -153,7 +182,7 @@ handle_info(Unexpected, State) ->
handle_event(#wx{event = #wxCommand{type = command_button_clicked}, handle_event(#wx{event = #wxCommand{type = command_button_clicked},
id = ID}, id = ID},
State = #s{buttons = Buttons}) -> State = #s{buttons = Buttons, wiz = none}) ->
NewState = NewState =
case lists:keyfind(ID, #w.id, Buttons) of case lists:keyfind(ID, #w.id, Buttons) of
#w{name = open} -> do_open(State); #w{name = open} -> do_open(State);
@ -166,17 +195,34 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked},
{noreply, NewState}; {noreply, NewState};
handle_event(#wx{event = #wxCommand{type = command_listbox_doubleclicked, handle_event(#wx{event = #wxCommand{type = command_listbox_doubleclicked,
commandInt = Selected}}, commandInt = Selected}},
State) -> State = #s{wiz = none}) ->
NewState = do_open2(Selected + 1, State), ok = do_open2(Selected + 1, State),
{noreply, NewState}; {noreply, State};
handle_event(#wx{event = #wxClose{}}, State) -> handle_event(#wx{event = #wxClose{}}, State = #s{wiz = none}) ->
ok = do_close(State), ok = do_close(State),
{noreply, State}; {noreply, State};
handle_event(#wx{event = #wxCommand{type = command_button_clicked},
id = ID},
State = #s{wiz = {_, WizButtons}}) ->
NewState =
case lists:keyfind(ID, #w.id, WizButtons) of
#w{name = noob} -> wiz_noob_assist(State);
#w{name = l33t} -> close_wiz(State);
false -> State
end,
{noreply, NewState};
handle_event(#wx{event = #wxClose{}}, State = #s{wiz = {_, _}}) ->
NewState = close_wiz(State),
{noreply, NewState};
handle_event(Event, State) -> handle_event(Event, State) ->
ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]), ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]),
{noreply, State}. {noreply, State}.
handle_troubling(#s{frame = Frame}, Info) ->
zxw:show_message(Frame, Info).
code_change(_, State, _) -> code_change(_, State, _) ->
{ok, State}. {ok, State}.
@ -197,6 +243,26 @@ do_show(Manifest, State = #s{picker = Picker}) ->
State#s{wallets = Manifest}. State#s{wallets = Manifest}.
do_first_run(State = #s{frame = Frame, wallets = Manifest}) ->
Count = length(Manifest),
if
Count =:= 0 ->
do_wiz(State);
Count =:= 1 ->
do_open(State);
Count > 1 ->
true = wxFrame:show(Frame),
wxFrame:raise(Frame),
State
end.
close_wiz(State = #s{frame = Frame, wiz = {Wiz, _}}) ->
ok = wxWindow:destroy(Wiz),
true = wxFrame:show(Frame),
State#s{wiz = none}.
do_close(#s{frame = Frame, prefs = Prefs}) -> do_close(#s{frame = Frame, prefs = Prefs}) ->
Geometry = Geometry =
case wxTopLevelWindow:isMaximized(Frame) of case wxTopLevelWindow:isMaximized(Frame) of
@ -219,10 +285,26 @@ handle_button(Name, State) ->
do_open(State = #s{wallets = []}) -> do_open(State = #s{wallets = []}) ->
State; State;
do_open(State = #s{wallets = [#wr{pass = true, path = Path}]}) ->
ok = do_open3(Path, State),
State;
do_open(State = #s{wallets = [#wr{pass = false, path = Path}], frame = Frame}) ->
ok =
case gd_con:open_wallet(Path, none) of
ok ->
do_close(State);
Error ->
ok = ensure_shown(Frame),
trouble(Error)
end,
State;
do_open(State = #s{picker = Picker}) -> do_open(State = #s{picker = Picker}) ->
case wxListBox:getSelection(Picker) of case wxListBox:getSelection(Picker) of
-1 -> State; -1 ->
Selected -> do_open2(Selected + 1, State) State;
Selected ->
ok = do_open2(Selected + 1, State),
State
end. end.
do_open2(Selected, State = #s{wallets = Wallets}) -> do_open2(Selected, State = #s{wallets = Wallets}) ->
@ -231,8 +313,7 @@ do_open2(Selected, State = #s{wallets = Wallets}) ->
do_open3(Path, State); do_open3(Path, State);
#wr{pass = false, path = Path} -> #wr{pass = false, path = Path} ->
ok = gd_con:open_wallet(Path, none), ok = gd_con:open_wallet(Path, none),
ok = do_close(State), do_close(State)
State
end. end.
do_open3(Path, State = #s{frame = Frame, j = J}) -> do_open3(Path, State = #s{frame = Frame, j = J}) ->
@ -250,32 +331,90 @@ do_open3(Path, State = #s{frame = Frame, j = J}) ->
ok = wxDialog:setSizer(Dialog, Sizer), ok = wxDialog:setSizer(Dialog, Sizer),
ok = wxBoxSizer:layout(Sizer), ok = wxBoxSizer:layout(Sizer),
ok = wxDialog:setSize(Dialog, {500, 130}), ok = wxDialog:setSize(Dialog, {500, 130}),
ok = wxFrame:center(Dialog), ok = wxDialog:center(Dialog),
ok = wxStyledTextCtrl:setFocus(PassTx), ok = wxStyledTextCtrl:setFocus(PassTx),
case wxDialog:showModal(Dialog) of case wxDialog:showModal(Dialog) of
?wxID_OK -> ?wxID_OK ->
case wxTextCtrl:getValue(PassTx) of case wxTextCtrl:getValue(PassTx) of
"" -> "" ->
ok = wxDialog:destroy(Dialog), ok = wxDialog:destroy(Dialog),
State; ensure_shown(Frame);
Phrase -> Phrase ->
ok = wxDialog:destroy(Dialog), ok = wxDialog:destroy(Dialog),
ok = gd_con:open_wallet(Path, Phrase), case gd_con:open_wallet(Path, Phrase) of
ok = do_close(State), ok ->
State do_close(State);
Error ->
ok = ensure_shown(Frame),
trouble(Error)
end
end; end;
?wxID_CANCEL -> ?wxID_CANCEL ->
ok = wxDialog:destroy(Dialog), ok = wxDialog:destroy(Dialog),
ensure_shown(Frame)
end.
do_wiz(State = #s{wx = WX, lang = Lang, wiz = none}) ->
Trans = gd_jt:read_translations(?MODULE),
J = gd_jt:j(Lang, Trans),
Wiz = wxFrame:new(WX, ?wxID_ANY, J("Initializing Wallet")),
MainSz = wxBoxSizer:new(?wxVERTICAL),
ButtonTemplates =
[{noob, J("I'm new!\nCreate a new account for me.")},
{l33t, J("Open the wallet manager.")}],
MakeButton =
fun({Name, Label}) ->
B = wxButton:new(Wiz, ?wxID_ANY, [{label, Label}]),
#w{name = Name, id = wxButton:getId(B), wx = B}
end,
Buttons = lists:map(MakeButton, ButtonTemplates),
Add = fun(#w{wx = Button}) -> wxBoxSizer:add(MainSz, Button, zxw:flags(wide)) end,
ok = lists:foreach(Add, Buttons),
ok = wxFrame:setSizer(Wiz, MainSz),
ok = wxSizer:layout(MainSz),
ok = wxFrame:connect(Wiz, command_button_clicked),
ok = wxFrame:connect(Wiz, close_window),
ok = wxFrame:setSize(Wiz, {300, 300}),
ok = wxFrame:center(Wiz),
true = wxFrame:show(Wiz),
State#s{wiz = {Wiz, Buttons}}.
wiz_noob_assist(State = #s{j = J, wiz = {Wiz, _}}) ->
DefaultDir = zx_lib:path(var, "otpr", "gajudesk"),
Name = default_name(),
Path = filename:join(DefaultDir, Name),
case do_new2(Path, J, Wiz) of
ok ->
Label = J("Account 1"),
ok = gd_con:make_key({eddsa, ed25519}, 256, Label, <<>>, none, {sha3, 256}),
ok = do_close(State),
State;
abort ->
State State
end. end.
default_name() ->
{{YY, MM, DD}, {Hr, Mn, Sc}} = calendar:local_time(),
Form = "~4.10.0B-~2.10.0B-~2.10.0B_~2.10.0B-~2.10.0B-~2.10.0B",
Name = io_lib:format(Form, [YY, MM, DD, Hr, Mn, Sc]),
unicode:characters_to_list(Name ++ ".gaju").
do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) -> do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")), DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")),
Options = Options =
[{message, J("Save Location")}, [{message, J("Save Location")},
{defaultDir, DefaultDir}, {defaultDir, DefaultDir},
{defaultFile, "default.gaju"}, {defaultFile, default_name()},
{wildCard, "*.gaju"}, {wildCard, "*.gaju"},
{sz, {300, 270}}, {sz, {300, 270}},
{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}], {style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}],
@ -289,7 +428,9 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
case do_new2(Path, J, Frame) of case do_new2(Path, J, Frame) of
ok -> ok ->
NewPrefs = maps:put(dir, Dir, Prefs), NewPrefs = maps:put(dir, Dir, Prefs),
do_close(State#s{prefs = NewPrefs}); NewState = State#s{prefs = NewPrefs},
ok = do_close(NewState),
NewState;
abort -> abort ->
State State
end; end;
@ -301,8 +442,8 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
do_new2(Path, J, Frame) -> do_new2(Path, J, Frame) ->
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]), Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]),
Sizer = wxBoxSizer:new(?wxVERTICAL), Sizer = wxBoxSizer:new(?wxVERTICAL),
NetworkOptions = [J("Mainnet"), J("Testnet")],
Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]), Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, NetworkOptions),
NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]), NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]),
NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY), NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
_ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)), _ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)),
@ -382,7 +523,9 @@ do_import(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
case do_import2(Dir, File, J, Frame) of case do_import2(Dir, File, J, Frame) of
ok -> ok ->
NewPrefs = maps:put(dir, Dir, Prefs), NewPrefs = maps:put(dir, Dir, Prefs),
do_close(State#s{prefs = NewPrefs}); NewState = State#s{prefs = NewPrefs},
ok = do_close(NewState),
NewState;
abort -> abort ->
State State
end; end;
@ -484,3 +627,13 @@ do_drop(Selected, State = #s{j = J, frame = Frame, wallets = Wallets}) ->
ok ok
end, end,
State. State.
ensure_shown(Frame) ->
case wxWindow:isShown(Frame) of
true ->
ok;
false ->
true = wxFrame:show(Frame),
ok
end.

View File

@ -4,7 +4,7 @@
{prefix,"gd"}. {prefix,"gd"}.
{author,"Craig Everett"}. {author,"Craig Everett"}.
{desc,"A desktop client for the Gajumaru network of blockchain networks"}. {desc,"A desktop client for the Gajumaru network of blockchain networks"}.
{package_id,{"otpr","gajudesk",{0,6,6}}}. {package_id,{"otpr","gajudesk",{0,7,0}}}.
{deps,[{"otpr","hakuzaru",{0,6,1}}, {deps,[{"otpr","hakuzaru",{0,6,1}},
{"otpr","eblake2",{1,0,1}}, {"otpr","eblake2",{1,0,1}},
{"otpr","base58",{0,1,1}}, {"otpr","base58",{0,1,1}},