diff --git a/ceo/tui/CeoFrame.py b/ceo/tui/CeoFrame.py index 1a30d21..87c1609 100644 --- a/ceo/tui/CeoFrame.py +++ b/ceo/tui/CeoFrame.py @@ -89,6 +89,17 @@ class CeoFrame(Frame): # frames which create layouts dynamically. self._layouts.clear() + def force_update(self): + """ + This should be called by background threads after they make changes + to the UI. + """ + # Since we're running in a separate thread, we need to force the + # screen to update. See + # https://github.com/peterbrittain/asciimatics/issues/56 + self.fix() + self._model.screen.force_update() + def add_buttons( self, back_btn=False, back_btn_text='Back', next_scene=None, next_btn_text='Next', on_next=None, diff --git a/ceo/tui/ConfirmView.py b/ceo/tui/ConfirmView.py index d3af5a9..9c01584 100644 --- a/ceo/tui/ConfirmView.py +++ b/ceo/tui/ConfirmView.py @@ -40,7 +40,7 @@ class ConfirmView(CeoFrame): 'back_btn': True, 'back_btn_text': 'No', 'next_btn_text': 'Yes', } if self._model.operations is not None: - kwargs['next_scene'] = 'Transaction' + kwargs['next_scene'] = self._model.txn_view_name or 'Transaction' else: self.add_flash_message_layout() kwargs['on_next_excl'] = self._next diff --git a/ceo/tui/Model.py b/ceo/tui/Model.py index 1173dde..08c9b38 100644 --- a/ceo/tui/Model.py +++ b/ceo/tui/Model.py @@ -10,6 +10,7 @@ class Model: self.title = None self.scene_stack = [] self.result_view_name = None + self.txn_view_name = None self.error_message = None # view-specific data, to be used when e.g. resizing the window self._initial_viewdata = { @@ -91,6 +92,7 @@ class Model: self.error_message = None self.scene_stack.clear() self.result_view_name = None + self.txn_view_name = None for view in self.views: if hasattr(view, '_ceoframe_on_reset'): view._ceoframe_on_reset() diff --git a/ceo/tui/TUIStreamResponseHandler.py b/ceo/tui/TUIStreamResponseHandler.py index 0c1bae4..4645dd2 100644 --- a/ceo/tui/TUIStreamResponseHandler.py +++ b/ceo/tui/TUIStreamResponseHandler.py @@ -25,11 +25,7 @@ class TUIStreamResponseHandler(StreamResponseHandler): self.error_messages = [] def _update(self): - # Since we're running in a separate thread, we need to force the - # screen to update. See - # https://github.com/peterbrittain/asciimatics/issues/56 - self.txn_view.fix() - self.screen.force_update() + self.txn_view.force_update() def _show_msg(self, msg: str = '\n'): for line in msg.splitlines(): diff --git a/ceo/tui/TransactionView.py b/ceo/tui/TransactionView.py index e8a959b..8aa3c45 100644 --- a/ceo/tui/TransactionView.py +++ b/ceo/tui/TransactionView.py @@ -1,4 +1,5 @@ from threading import Thread +from typing import List, Dict from asciimatics.exceptions import NextScene from asciimatics.widgets import Layout, Button, Divider, Label @@ -16,7 +17,6 @@ class TransactionView(CeoFrame): on_load=self._txnview_on_load, title='Running Transaction', has_dynamic_layouts=True, ) - self._model = model # map operation names to label widgets self._labels = model.viewdata['Transaction']['labels'] @@ -87,7 +87,12 @@ class TransactionView(CeoFrame): msg_layout=self._msg_layout, txn_view=self, ) - generic_handle_stream_response(resp, self._model.operations, handler) + data = generic_handle_stream_response(resp, self._model.operations, handler) + self.write_extra_txn_info(data) + + # to be overridden in child classes if desired + def write_extra_txn_info(self, data: List[Dict]): + pass def enable_next_btn(self): self._next_btn.disabled = False diff --git a/ceo/tui/members/AddUserTransactionView.py b/ceo/tui/members/AddUserTransactionView.py new file mode 100644 index 0000000..f3b9cda --- /dev/null +++ b/ceo/tui/members/AddUserTransactionView.py @@ -0,0 +1,25 @@ +from typing import List, Dict + +from asciimatics.widgets import Label + +from ...utils import get_failed_operations +from ..TransactionView import TransactionView + + +class AddUserTransactionView(TransactionView): + def _show_msg(self, msg: str = '\n'): + for line in msg.splitlines(): + self._msg_layout.add_widget(Label(line, align='^')) + + def write_extra_txn_info(self, data: List[Dict]): + if data[-1]['status'] != 'completed': + return + result = data[-1]['result'] + failed_operations = get_failed_operations(data) + self._show_msg() + self._show_msg('User password is: ' + result['password']) + if 'send_welcome_message' in failed_operations: + self._show_msg() + self._show_msg('Since the welcome message was not sent,') + self._show_msg('you need to email this password to the user.') + self.force_update() diff --git a/ceo/tui/members/AddUserView.py b/ceo/tui/members/AddUserView.py index 8bde496..63878b2 100644 --- a/ceo/tui/members/AddUserView.py +++ b/ceo/tui/members/AddUserView.py @@ -93,3 +93,4 @@ class AddUserView(CeoFrame): self._model.deferred_req = defer(http_post, '/api/members', json=body) self._model.operations = get_adduser_operations(body) + self._model.txn_view_name = 'AddUserTransaction' diff --git a/ceo/tui/start.py b/ceo/tui/start.py index df88af2..a7bf5a4 100644 --- a/ceo/tui/start.py +++ b/ceo/tui/start.py @@ -19,6 +19,7 @@ from .groups.AddMemberToGroupView import AddMemberToGroupView from .groups.GetGroupView import GetGroupView from .groups.GetGroupResultView import GetGroupResultView from .groups.RemoveMemberFromGroupView import RemoveMemberFromGroupView +from .members.AddUserTransactionView import AddUserTransactionView from .members.AddUserView import AddUserView from .members.ChangeLoginShellView import ChangeLoginShellView from .members.GetUserView import GetUserView @@ -48,6 +49,7 @@ def screen_wrapper(screen, last_scene, model): ('Result', ResultView(screen, width, height, model)), ('Error', ErrorView(screen, width, height, model)), ('AddUser', AddUserView(screen, width, height, model)), + ('AddUserTransaction', AddUserTransactionView(screen, width, height, model)), ('RenewUser', RenewUserView(screen, width, height, model)), ('GetUser', GetUserView(screen, width, height, model)), ('GetUserResult', GetUserResultView(screen, width, height, model)),