From f92c5fbde0cb6c76072a058d0f286ce50b61faeb Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Thu, 7 Aug 2025 15:47:05 +0900 Subject: [PATCH] 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. https://git.qpq.swiss/QPQ-AG/GajuDesk/issues/18 https://git.qpq.swiss/QPQ-AG/GajuDesk/issues/19 Reviewed-on: https://git.qpq.swiss/QPQ-AG/GajuDesk/pulls/24 Reviewed-by: Ulf Wiger Co-authored-by: Craig Everett Co-committed-by: Craig Everett --- ebin/gajudesk.app | 2 +- src/gajudesk.erl | 2 +- src/gd_con.erl | 62 ++++++----- src/gd_grids.erl | 4 +- src/gd_gui.erl | 3 +- src/gd_jt.erl | 2 +- src/gd_sophia_editor.erl | 2 +- src/gd_sup.erl | 2 +- src/gd_v.erl | 2 +- src/gd_v_devman.erl | 2 +- src/gd_v_netman.erl | 2 +- src/gd_v_wallman.erl | 229 ++++++++++++++++++++++++++++++++------- zomp.meta | 2 +- 13 files changed, 236 insertions(+), 80 deletions(-) diff --git a/ebin/gajudesk.app b/ebin/gajudesk.app index 1f70874..676fc99 100644 --- a/ebin/gajudesk.app +++ b/ebin/gajudesk.app @@ -3,7 +3,7 @@ {registered,[]}, {included_applications,[]}, {applications,[stdlib,kernel,sasl,ssl]}, - {vsn,"0.6.6"}, + {vsn,"0.7.0"}, {modules,[gajudesk,gd_con,gd_grids,gd_gui,gd_jt, gd_sophia_editor,gd_sup,gd_v,gd_v_devman,gd_v_netman, gd_v_wallman]}, diff --git a/src/gajudesk.erl b/src/gajudesk.erl index 995acf3..6ae95a4 100644 --- a/src/gajudesk.erl +++ b/src/gajudesk.erl @@ -3,7 +3,7 @@ %%% @end -module(gajudesk). --vsn("0.6.6"). +-vsn("0.7.0"). -behavior(application). -author("Craig Everett "). -copyright("QPQ AG "). diff --git a/src/gd_con.erl b/src/gd_con.erl index 75c20e4..6bb2e63 100644 --- a/src/gd_con.erl +++ b/src/gd_con.erl @@ -3,7 +3,7 @@ %%% @end -module(gd_con). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). @@ -38,15 +38,15 @@ mon = none :: none | reference()}). -record(s, - {version = 1 :: integer(), - window = none :: none | wx:wx_object(), - timer = none :: none | reference(), - tasks = [] :: [#ui{}], - selected = 0 :: non_neg_integer(), - wallet = none :: none | #wallet{}, - pass = none :: none | binary(), - prefs = #{} :: #{module() := term()}, - wallets = [] :: [#wr{}]}). + {version = 1 :: integer(), + window = none :: none | wx:wx_object(), + timer = none :: none | reference(), + tasks = [] :: [#ui{}], + selected = 0 :: non_neg_integer(), + wallet = none :: none | #wallet{}, + pass = none :: none | binary(), + prefs = #{} :: #{module() := term()}, + wallets = [] :: [#wr{}]}). -type state() :: #s{}. @@ -63,15 +63,16 @@ when Name :: 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(), - Phrase :: string(). + Phrase :: string(), + Result :: ok | {error, Reason :: term()}. open_wallet(Path, Phrase) -> - gen_server:cast(?MODULE, {open_wallet, Path, Phrase}). + gen_server:call(?MODULE, {open_wallet, Path, Phrase}). -spec close_wallet() -> ok. @@ -217,7 +218,7 @@ deploy(Build, Params, InitArgs) -> Size :: 256, Name :: string(), Seed :: string(), - Encoding :: utf8 | base64 | base58, + Encoding :: utf8 | base64 | base58 | none, Transform :: {Algo, Yugeness}, Algo :: sha3 | sha2 | x_or | pbkdf2, Yugeness :: rand | non_neg_integer(). @@ -328,6 +329,7 @@ init(none) -> T = erlang:send_after(tic(), self(), tic), State = #s{window = Window, timer = T, wallets = Wallets, prefs = Prefs}, NewState = do_show_ui(gd_v_wallman, State), + ok = gd_v_wallman:first_run(), {ok, NewState}. @@ -365,6 +367,9 @@ handle_call(list_keys, _, State) -> handle_call({nonce, ID}, _, State) -> Response = do_nonce(ID), {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) -> Response = do_network(State), {reply, Response, State}; @@ -374,6 +379,9 @@ handle_call({save, Module, Prefs}, _, State) -> handle_call({mnemonic, ID}, _, State) -> Response = do_mnemonic(ID, State), {reply, Response, State}; +handle_call({show_ui, Name}, _, State) -> + NewState = do_show_ui(Name, State), + {reply, ok, NewState}; handle_call(Unexpected, From, State) -> ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), {noreply, State}. @@ -387,16 +395,11 @@ handle_call(Unexpected, From, State) -> %% The gen_server:handle_cast/2 callback. %% 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) -> NextState = do_close_wallet(State), ok = gd_gui:show([]), NewState = do_show_ui(gd_v_wallman, NextState), + ok = gd_v_wallman:to_front(), {noreply, NewState}; handle_cast({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) -> NewState = do_tic(State), {noreply, NewState}; +handle_info({show_ui, Name}, State) -> + NewState = do_show_ui(Name, State), + {noreply, NewState}; handle_info({'DOWN', Mon, process, PID, Info}, State) -> NewState = handle_down(Mon, PID, Info, State), {noreply, NewState}; @@ -531,7 +537,6 @@ do_show_ui(Name, State = #s{tasks = Tasks, prefs = Prefs}) -> Win = Name:start_link({TaskPrefs, TaskData}), PID = wx_object:get_pid(Win), Mon = monitor(process, PID), - ok = Name:to_front(Win), UI = #ui{name = Name, pid = PID, wx = Win, mon = Mon}, State#s{tasks = [UI | Tasks]} end. @@ -863,7 +868,9 @@ do_make_key(Name, Seed, base58, Transform, State) -> do_make_key2(_, _, _, State = #s{wallet = none}) -> 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, State = #s{wallet = Current, wallets = Wallets, pass = Pass}) -> #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); Error -> gd_gui:trouble(Error) end, - State#s{pass = Pass, wallet = Recovered}; + {ok, State#s{pass = Pass, wallet = Recovered}}; Error -> - ok = gd_gui:trouble(Error), - New = default_wallet(), - State#s{wallet = New} + {Error, State} end. -default_wallet() -> - default_wallet(testnet). - default_wallet(mainnet) -> Node = #node{ip = "groot.mainnet.gajumaru.io"}, Groot = #chain{id = <<"groot.mainnet">>, diff --git a/src/gd_grids.erl b/src/gd_grids.erl index 7a5f3f0..a639e30 100644 --- a/src/gd_grids.erl +++ b/src/gd_grids.erl @@ -37,7 +37,7 @@ %%% @end -module(gd_grids). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). @@ -59,7 +59,7 @@ Reason :: bad_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"} -> spend(R, chain, list_to_binary(H), Q); #{path := "/1/s/" ++ R, host := H, query := Q, scheme := "grid"} -> diff --git a/src/gd_gui.erl b/src/gd_gui.erl index 0e91dae..3b583aa 100644 --- a/src/gd_gui.erl +++ b/src/gd_gui.erl @@ -3,7 +3,7 @@ %%% @end -module(gd_gui). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). @@ -361,6 +361,7 @@ refresh(State) -> wallman(State) -> ok = gd_con:show_ui(gd_v_wallman), + ok = gd_v_wallman:to_front(), State. diff --git a/src/gd_jt.erl b/src/gd_jt.erl index d0e4858..6293b29 100644 --- a/src/gd_jt.erl +++ b/src/gd_jt.erl @@ -15,7 +15,7 @@ %%% translation library is retained). -module(gd_jt). --vsn("0.6.6"). +-vsn("0.7.0"). -export([read_translations/1, j/2, oneshot_j/2]). diff --git a/src/gd_sophia_editor.erl b/src/gd_sophia_editor.erl index 1529b60..448bb7c 100644 --- a/src/gd_sophia_editor.erl +++ b/src/gd_sophia_editor.erl @@ -1,5 +1,5 @@ -module(gd_sophia_editor). --vsn("0.6.6"). +-vsn("0.7.0"). -export([new/1, update/2, get_text/1, set_text/2]). diff --git a/src/gd_sup.erl b/src/gd_sup.erl index f3c9810..5f92335 100644 --- a/src/gd_sup.erl +++ b/src/gd_sup.erl @@ -12,7 +12,7 @@ %%% @end -module(gd_sup). --vsn("0.6.6"). +-vsn("0.7.0"). -behaviour(supervisor). -author("Craig Everett "). -copyright("QPQ AG "). diff --git a/src/gd_v.erl b/src/gd_v.erl index 5908de5..c93be9b 100644 --- a/src/gd_v.erl +++ b/src/gd_v.erl @@ -1,5 +1,5 @@ -module(gd_v). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). diff --git a/src/gd_v_devman.erl b/src/gd_v_devman.erl index c2fa0a8..a5dad28 100644 --- a/src/gd_v_devman.erl +++ b/src/gd_v_devman.erl @@ -1,5 +1,5 @@ -module(gd_v_devman). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). diff --git a/src/gd_v_netman.erl b/src/gd_v_netman.erl index 4de8e5e..000688a 100644 --- a/src/gd_v_netman.erl +++ b/src/gd_v_netman.erl @@ -1,5 +1,5 @@ -module(gd_v_netman). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). diff --git a/src/gd_v_wallman.erl b/src/gd_v_wallman.erl index 49f007e..cdf8514 100644 --- a/src/gd_v_wallman.erl +++ b/src/gd_v_wallman.erl @@ -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). --vsn("0.6.6"). +-vsn("0.7.0"). -author("Craig Everett "). -copyright("QPQ AG "). -license("GPL-3.0-or-later"). @@ -7,7 +21,7 @@ -behavior(wx_object). %-behavior(gd_v). -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([start_link/1]). -export([init/1, terminate/2, code_change/3, @@ -29,11 +43,18 @@ prefs = #{} :: map(), wallets = [] :: [#wr{}], picker = none :: none | wx:wx_object(), - buttons = [] :: [#w{}]}). + buttons = [] :: [#w{}], + wiz = none :: none | {wx:wx_object(), Buttons :: [#w{}]}}). %%% Interface +-spec to_front() -> ok. + +to_front() -> + wx_object:cast(?MODULE, to_front). + + -spec to_front(Win) -> ok when Win :: wx:wx_object(). @@ -41,6 +62,19 @@ to_front(Win) -> 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 when Win :: wx:xw_object(), Manifest :: [#wr{}]. @@ -107,34 +141,32 @@ init({Prefs, Manifest}) -> ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, close_window), - true = wxFrame:show(Frame), 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, - wallets = Manifest, - picker = Picker, - buttons = Buttons}, + wallets = Manifest, + picker = Picker, + buttons = Buttons}, {Frame, State}. + %%% wx_object handle_call(Unexpected, From, State) -> ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), {noreply, State}. - handle_cast(to_front, State = #s{frame = Frame}) -> + ok = ensure_shown(Frame), ok = wxFrame:raise(Frame), {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) -> NewState = do_show(Manifest, State), {noreply, NewState}; @@ -143,9 +175,6 @@ handle_cast(Unexpected, State) -> {noreply, State}. -handle_info(new, State) -> - NewState = do_new(State), - {noreply, NewState}; handle_info(Unexpected, State) -> ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]), {noreply, State}. @@ -153,7 +182,7 @@ handle_info(Unexpected, State) -> handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID}, - State = #s{buttons = Buttons}) -> + State = #s{buttons = Buttons, wiz = none}) -> NewState = case lists:keyfind(ID, #w.id, Buttons) of #w{name = open} -> do_open(State); @@ -166,17 +195,34 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked}, {noreply, NewState}; handle_event(#wx{event = #wxCommand{type = command_listbox_doubleclicked, commandInt = Selected}}, - State) -> - NewState = do_open2(Selected + 1, State), - {noreply, NewState}; -handle_event(#wx{event = #wxClose{}}, State) -> + State = #s{wiz = none}) -> + ok = do_open2(Selected + 1, State), + {noreply, State}; +handle_event(#wx{event = #wxClose{}}, State = #s{wiz = none}) -> ok = do_close(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) -> ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]), {noreply, State}. +handle_troubling(#s{frame = Frame}, Info) -> + zxw:show_message(Frame, Info). + + code_change(_, State, _) -> {ok, State}. @@ -197,6 +243,26 @@ do_show(Manifest, State = #s{picker = Picker}) -> 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}) -> Geometry = case wxTopLevelWindow:isMaximized(Frame) of @@ -219,10 +285,26 @@ handle_button(Name, State) -> do_open(State = #s{wallets = []}) -> 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}) -> case wxListBox:getSelection(Picker) of - -1 -> State; - Selected -> do_open2(Selected + 1, State) + -1 -> + State; + Selected -> + ok = do_open2(Selected + 1, State), + State end. do_open2(Selected, State = #s{wallets = Wallets}) -> @@ -231,8 +313,7 @@ do_open2(Selected, State = #s{wallets = Wallets}) -> do_open3(Path, State); #wr{pass = false, path = Path} -> ok = gd_con:open_wallet(Path, none), - ok = do_close(State), - State + do_close(State) end. 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 = wxBoxSizer:layout(Sizer), ok = wxDialog:setSize(Dialog, {500, 130}), - ok = wxFrame:center(Dialog), + ok = wxDialog:center(Dialog), ok = wxStyledTextCtrl:setFocus(PassTx), case wxDialog:showModal(Dialog) of ?wxID_OK -> case wxTextCtrl:getValue(PassTx) of "" -> ok = wxDialog:destroy(Dialog), - State; + ensure_shown(Frame); Phrase -> ok = wxDialog:destroy(Dialog), - ok = gd_con:open_wallet(Path, Phrase), - ok = do_close(State), - State + case gd_con:open_wallet(Path, Phrase) of + ok -> + do_close(State); + Error -> + ok = ensure_shown(Frame), + trouble(Error) + end end; ?wxID_CANCEL -> 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 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}) -> DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")), Options = [{message, J("Save Location")}, {defaultDir, DefaultDir}, - {defaultFile, "default.gaju"}, + {defaultFile, default_name()}, {wildCard, "*.gaju"}, {sz, {300, 270}}, {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 ok -> NewPrefs = maps:put(dir, Dir, Prefs), - do_close(State#s{prefs = NewPrefs}); + NewState = State#s{prefs = NewPrefs}, + ok = do_close(NewState), + NewState; abort -> State end; @@ -301,8 +442,8 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) -> do_new2(Path, J, Frame) -> Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]), Sizer = wxBoxSizer:new(?wxVERTICAL), - - Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]), + NetworkOptions = [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")}]), NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY), _ = 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 ok -> NewPrefs = maps:put(dir, Dir, Prefs), - do_close(State#s{prefs = NewPrefs}); + NewState = State#s{prefs = NewPrefs}, + ok = do_close(NewState), + NewState; abort -> State end; @@ -484,3 +627,13 @@ do_drop(Selected, State = #s{j = J, frame = Frame, wallets = Wallets}) -> ok end, State. + + +ensure_shown(Frame) -> + case wxWindow:isShown(Frame) of + true -> + ok; + false -> + true = wxFrame:show(Frame), + ok + end. diff --git a/zomp.meta b/zomp.meta index 8f43620..7e49fef 100644 --- a/zomp.meta +++ b/zomp.meta @@ -4,7 +4,7 @@ {prefix,"gd"}. {author,"Craig Everett"}. {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}}, {"otpr","eblake2",{1,0,1}}, {"otpr","base58",{0,1,1}},