diff --git a/res/ui/community_view.ui b/res/ui/community_view.ui deleted file mode 100644 index 8827fe266a1f83882dcac7b74cab8c299502e0db..0000000000000000000000000000000000000000 --- a/res/ui/community_view.ui +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>CommunityWidget</class> - <widget class="QWidget" name="CommunityWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>624</width> - <height>429</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTabWidget" name="tabs"/> - </item> - </layout> - </widget> - <resources> - <include location="../icons/icons.qrc"/> - </resources> - <connections/> -</ui> diff --git a/src/sakia/gui/certification/controller.py b/src/sakia/gui/certification/controller.py index 4d9171a1ede6807adc694ef5970efc8185a13bf4..94aef99deaa17eff8054ff14acc8af2e23569e4a 100644 --- a/src/sakia/gui/certification/controller.py +++ b/src/sakia/gui/certification/controller.py @@ -128,11 +128,12 @@ class CertificationController(ComponentController): """ Validate the dialog """ + self.view.button_box.setDisabled(True) pubkey = self.selected_pubkey() if pubkey: password = await self.password_asker.async_exec() if password == "": - self.view.enable_button_box() + self.view.button_box.setEnabled(True) return QApplication.setOverrideCursor(Qt.WaitCursor) result = await self.account.certify(password, self.community, pubkey) @@ -143,7 +144,7 @@ class CertificationController(ComponentController): else: await self.view.show_error(result[1]) QApplication.restoreOverrideCursor() - self.view.enable_button_box() + self.view.button_box.setEnabled(True) def reject(self): self.view.reject() @@ -200,7 +201,7 @@ class CertificationController(ComponentController): def change_current_community(self, index): self.model.change_community(index) self.search_user.set_community(self.community) - self.member_widget.change_community(self.community) + self.user_information.change_community(self.community) self.refresh() def async_exec(self): diff --git a/src/sakia/gui/main_window/controller.py b/src/sakia/gui/main_window/controller.py index bdde74da84c7f5465b4b5d65acabff7349e1ddfa..b29a7b238683bb2ba4d9c4319ef38fcdb90415e5 100644 --- a/src/sakia/gui/main_window/controller.py +++ b/src/sakia/gui/main_window/controller.py @@ -64,7 +64,7 @@ class MainWindowController(ComponentController): main_window.navigation = main_window.attach(NavigationController.create(main_window, app)) view.bottom_layout.insertWidget(0, main_window.navigation._view) main_window.navigation.community_changed.connect(main_window.handle_community_change) - main_window.navigation.account_changed.connect(main_window.handle_community_change) + main_window.navigation.account_changed.connect(main_window.handle_account_change) main_window.toolbar = main_window.attach(ToolbarController.create(main_window, app, app.current_account, None, @@ -116,14 +116,15 @@ class MainWindowController(ComponentController): Set current account :param sakia.core.Account account: """ - self.toolbar.set_account(account) + self.toolbar.account = account + self.password_asker.change_account(account) def handle_community_change(self, community): """ Set current community :param sakia.core.Community community: """ - self.toolbar.set_community(community) + self.toolbar.community = community def refresh(self): """ diff --git a/src/sakia/gui/navigation/navigation.ui b/src/sakia/gui/navigation/navigation.ui index 0da57f788436ddbfe80153f08eda624ea2ad030f..266315e5655d3fb9fd7c34bf21de42db92fa99ce 100644 --- a/src/sakia/gui/navigation/navigation.ui +++ b/src/sakia/gui/navigation/navigation.ui @@ -28,6 +28,18 @@ <verstretch>0</verstretch> </sizepolicy> </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="showDropIndicator" stdset="0"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>true</bool> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> </widget> </item> <item> diff --git a/src/sakia/gui/navigation/view.py b/src/sakia/gui/navigation/view.py index 4518017f0b25ef779e55641a3b9d3b2762b6fe1a..a822e992c19fb117bda9e10bbf926fa602fcd5ab 100644 --- a/src/sakia/gui/navigation/view.py +++ b/src/sakia/gui/navigation/view.py @@ -21,6 +21,7 @@ class NavigationView(QFrame, Ui_Navigation): :param sakia.gui.navigation.model.NavigationModel """ self.tree_view.setModel(model.generic_tree()) + self.tree_view.expandAll() def add_widget(self, widget): """ diff --git a/src/sakia/gui/toolbar/controller.py b/src/sakia/gui/toolbar/controller.py index b9622b26b719ce238c9ebeba69fcbf0c6462dd5a..c44950b0eb22253f7aa37055ef4e141112f1eecb 100644 --- a/src/sakia/gui/toolbar/controller.py +++ b/src/sakia/gui/toolbar/controller.py @@ -7,6 +7,7 @@ from ...tools.decorators import asyncify, once_at_a_time, cancel_once_task from ..widgets.dialogs import QAsyncMessageBox, QAsyncFileDialog, dialog_async_exec from ..widgets import toast from ..certification.controller import CertificationController +from ..transfer.controller import TransferController import logging @@ -199,13 +200,10 @@ The process to join back the community later will have to be done again.""") self.account) def open_transfer_money_dialog(self): - dialog = TransferMoneyDialog(self.app, - self.account, - self.password_asker, - self.community_view.community, - None) - if dialog.exec() == QDialog.Accepted: - self.community_view.tab_history.table_history.model().sourceModel().refresh_transfers() + dialog = TransferController.open_dialog(self, self.model.app, + account=self.model.account, + password_asker=self.password_asker, + community=self.model.community) def retranslateUi(self, widget): """ diff --git a/src/sakia/gui/transfer/__init__.py b/src/sakia/gui/transfer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/sakia/gui/transfer/controller.py b/src/sakia/gui/transfer/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..33fc5660058f454e2589ce4315b58f642c026c65 --- /dev/null +++ b/src/sakia/gui/transfer/controller.py @@ -0,0 +1,247 @@ +from ..component.controller import ComponentController +from ..search_user.controller import SearchUserController +from ..user_information.controller import UserInformationController +from .view import TransferView +from .model import TransferModel +from sakia.tools.decorators import asyncify +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +import logging +import asyncio + + +class TransferController(ComponentController): + """ + The transfer component controller + """ + + def __init__(self, parent, view, model, search_user, user_information, password_asker): + """ + Constructor of the transfer component + + :param sakia.gui.transfer.view.TransferView: the view + :param sakia.gui.transfer.model.TransferModel model: the model + """ + super().__init__(parent, view, model) + self.password_asker = password_asker + self.search_user = search_user + self.user_information = user_information + self.view.button_box.accepted.connect(self.accept) + self.view.button_box.rejected.connect(self.reject) + self.view.combo_community.currentIndexChanged.connect(self.change_current_community) + self.view.combo_wallets.currentIndexChanged.connect(self.change_current_wallet) + self.view.spinbox_amount.valueChanged.connect(self.handle_amount_change) + self.view.spinbox_relative.valueChanged.connect(self.handle_relative_change) + + @classmethod + def create(cls, parent, app, **kwargs): + """ + Instanciate a transfer component + :param sakia.gui.component.controller.ComponentController parent: + :param sakia.core.Application app: + :return: a new Transfer controller + :rtype: TransferController + """ + account = kwargs['account'] + community = kwargs['community'] + transfer = kwargs['transfer'] + password_asker = kwargs['password_asker'] + communities_names = [c.name for c in account.communities] + wallets_names = [w.name for w in account.wallets] + contacts_names = [c['name'] for c in account.contacts] + + view = TransferView(parent.view, None, None, communities_names, contacts_names, wallets_names) + model = TransferModel(None, app, account=account, community=community, resent_transfer=transfer) + transfer = cls(parent, view, model, None, None, password_asker) + + search_user = SearchUserController.create(transfer, app, + account=model.account, + community=model.community) + transfer.set_search_user(search_user) + + user_information = UserInformationController.create(transfer, app, + account=model.account, + community=model.community, + identity=None) + transfer.set_user_information(user_information) + model.setParent(transfer) + return transfer + + @classmethod + def open_dialog(cls, parent, app, account, password_asker, community): + dialog = cls.create(parent, app, + account=account, + password_asker=password_asker, + community=community, + transfer=None) + return dialog.exec() + + @classmethod + async def send_money_to_identity(cls, parent, app, account, password_asker, community, identity): + dialog = cls.create(parent, app, + account=account, + password_asker=password_asker, + community=community, + transfer=None) + dialog.view.edit_pubkey.setText(identity.pubkey) + dialog.view.radio_pubkey.setChecked(True) + return await dialog.async_exec() + + @classmethod + async def send_transfer_again(cls, parent, app, account, password_asker, community, resent_transfer): + dialog = cls.create(parent, app, + account=account, + password_asker=password_asker, + community=community, + resent_transfer=resent_transfer) + relative = await dialog.model.quant_to_rel(resent_transfer.metadata['amount']) + dialog.view.set_spinboxes_parameters(1, resent_transfer.metadata['amount'], relative) + dialog.view.change_relative_amount(relative) + dialog.view.change_quantitative_amount(resent_transfer.metadata['amount']) + + account = resent_transfer.metadata['issuer'] + wallet_index = [w.pubkey for w in app.current_account.wallets].index(account) + dialog.view.combo_wallets.setCurrentIndex(wallet_index) + dialog.view.edit_pubkey.setText(resent_transfer.metadata['receiver']) + dialog.view.radio_pubkey.setChecked(True) + dialog.view.edit_message.setText(resent_transfer.metadata['comment']) + + return await dialog.async_exec() + + @property + def view(self) -> TransferView: + return self._view + + @property + def model(self) -> TransferModel: + return self._model + + def set_search_user(self, search_user): + """ + + :param search_user: + :return: + """ + self.search_user = search_user + self.view.set_search_user(search_user.view) + search_user.identity_selected.connect(self.refresh_user_information) + + def set_user_information(self, user_information): + """ + + :param user_information: + :return: + """ + self.user_information = user_information + self.view.set_user_information(user_information.view) + + def refresh_user_information(self): + """ + Refresh user information + """ + pubkey = self.selected_pubkey() + self.user_information.search_identity(pubkey) + + def selected_pubkey(self): + """ + Get selected pubkey in the widgets of the window + :return: the current pubkey + :rtype: str + """ + pubkey = None + + if self.view.recipient_mode() == TransferView.RecipientMode.CONTACT: + contact_name = self.view.selected_contact() + pubkey = self.model.contact_name_pubkey(contact_name) + elif self.view.recipient_mode() == TransferView.RecipientMode.SEARCH: + if self.search_user.current_identity(): + pubkey = self.search_user.current_identity().pubkey + else: + pubkey = self.view.pubkey_value() + return pubkey + + @asyncify + async def accept(self): + logging.debug("Accept transfer action...") + self.view.button_box.setEnabled(False) + comment = self.view.edit_message.text() + + logging.debug("checking recipient mode...") + recipient = self.selected_pubkey() + amount = self.view.spinbox_amount.value() + + logging.debug("Showing password dialog...") + password = await self.password_asker.async_exec() + if password == "": + self.view.button_box.setEnabled(True) + return + + logging.debug("Setting cursor...") + QApplication.setOverrideCursor(Qt.WaitCursor) + + logging.debug("Send money...") + result = await self.model.send_money(recipient, amount, comment, password) + if result[0]: + await self.view.show_success(self.model.app.preferences['notifications'], recipient) + logging.debug("Restore cursor...") + QApplication.restoreOverrideCursor() + + # If we sent back a transaction we cancel the first one + self.model.cancel_previous() + self.model.app.refresh_transfers.emit() + self.view.accept() + else: + await self.view.show_error(self.model.app.preferences['notifications'], result[1]) + + QApplication.restoreOverrideCursor() + self.view.button_box.setEnabled(True) + + def reject(self): + self.view.reject() + + @asyncify + async def refresh(self): + amount = await self.model.wallet_value() + total_text = await self.model.localized_amount(amount) + self.view.refresh_labels(total_text, self.model.community.currency) + + if amount == 0: + self.view.set_button_box(TransferView.ButtonBoxState.NO_AMOUNT) + else: + self.view.set_button_box(TransferView.ButtonBoxState.OK) + + max_relative = await self.model.quant_to_rel(amount) + current_base = await self.model.current_base() + + self.view.set_spinboxes_parameters(pow(10, current_base), amount, max_relative) + + @asyncify + async def handle_amount_change(self, value): + relative = await self.model.quant_to_rel(value) + self.view.change_relative_amount(relative) + + @asyncify + async def handle_relative_change(self, value): + amount = await self.model.rel_to_quant(value) + self.view.change_quantitative_amount(amount) + + def change_current_community(self, index): + self.model.change_community(index) + self.search_user.set_community(self.community) + self.user_information.change_community(self.community) + self.refresh() + + def change_current_wallet(self, index): + self.model.change_wallet(index) + self.refresh() + + def async_exec(self): + future = asyncio.Future() + self.view.finished.connect(lambda r: future.set_result(r)) + self.view.open() + self.refresh() + return future + + def exec(self): + self.refresh() + self.view.exec() \ No newline at end of file diff --git a/src/sakia/gui/transfer/model.py b/src/sakia/gui/transfer/model.py new file mode 100644 index 0000000000000000000000000000000000000000..10d306a21015c4a9b177a2f38e9719b4d7c20a90 --- /dev/null +++ b/src/sakia/gui/transfer/model.py @@ -0,0 +1,118 @@ +from sakia.gui.component.model import ComponentModel + + +class TransferModel(ComponentModel): + """ + The model of transfer component + """ + + def __init__(self, parent, app, account, community, resent_transfer): + super().__init__(parent) + self.app = app + self.account = account + self.resent_transfer = resent_transfer + self.community = community if community else self.account.communities[0] + self.wallet = self.account.wallets[0] + + def contact_name_pubkey(self, name): + """ + Get the pubkey of a contact from its name + :param str name: + :return: + :rtype: str + """ + for contact in self.account.contacts: + if contact['name'] == name: + return contact['pubkey'] + + async def rel_to_quant(self, rel_value): + """ + Get the quantitative value of a relative amount + :param float rel_value: + :rtype: int + """ + ud_block = await self.community.get_ud_block() + if ud_block: + dividend = ud_block['dividend'] + base = ud_block['unitbase'] + else: + dividend = 1 + base = 0 + amount = rel_value * dividend * pow(10, base) + # amount is rounded to the nearest power of 10 depending of last ud base + rounded = int(pow(10, base) * round(float(amount) / pow(10, base))) + return rounded + + async def quant_to_rel(self, amount): + """ + Get the relative value of a given amount + :param int amount: + :rtype: float + """ + + ud_block = await self.community.get_ud_block() + if ud_block: + dividend = ud_block['dividend'] + base = ud_block['unitbase'] + else: + dividend = 1 + base = 0 + relative = amount / (dividend * pow(10, base)) + return relative + + async def wallet_value(self): + """ + Get the value of the current wallet in the current community + """ + return await self.wallet.value(self.community) + + async def current_base(self): + """ + Get the current base of the network + """ + ud_block = await self.community.get_ud_block() + if ud_block: + base = ud_block['unitbase'] + else: + base = 0 + return base + + async def localized_amount(self, amount): + """ + Get the value of the current referential + """ + localized = await self.account.current_ref.instance(amount, self.community, self.app) \ + .diff_localized(units=True, + international_system=self.app.preferences['international_system_of_units']) + return localized + + def change_community(self, index): + """ + Change the current community + :param int index: index in the list of communities + """ + self.community = self.account.communities[index] + + def change_wallet(self, index): + """ + Change the current wallet + :param int index: index in the list of wallets + """ + self.wallet = self.account.wallets[index] + + def cancel_previous(self): + if self.resent_transfer: + self.resent_transfer.cancel() + + async def send_money(self, recipient, amount, comment, password): + """ + Send money to given recipient using the account + :param str recipient: + :param int amount: + :param str comment: + :param str password: + :return: the result of the send + """ + + return await self.wallet.send_money(self.account.salt, password, self.community, + recipient, amount, comment) \ No newline at end of file diff --git a/src/sakia/gui/transfer.py b/src/sakia/gui/transfer/transfer.py similarity index 96% rename from src/sakia/gui/transfer.py rename to src/sakia/gui/transfer/transfer.py index 90a07f08822ad102b97e95a926fec53a4ff90df8..39bf940c96bd457aa54341b5ce62ba288486c92c 100644 --- a/src/sakia/gui/transfer.py +++ b/src/sakia/gui/transfer/transfer.py @@ -195,15 +195,6 @@ class TransferMoneyDialog(QObject): @asyncify async def relative_amount_changed(self, value): - ud_block = await self.community.get_ud_block() - if ud_block: - dividend = ud_block['dividend'] - base = ud_block['unitbase'] - else: - dividend = 1 - base = 0 - amount = value * dividend * pow(10, base) - amount = int(pow(10, base) * round(float(amount) / pow(10, base))) self.ui.spinbox_amount.blockSignals(True) self.ui.spinbox_amount.setValue(amount) self.ui.spinbox_amount.blockSignals(False) diff --git a/res/ui/transfer.ui b/src/sakia/gui/transfer/transfer.ui similarity index 50% rename from res/ui/transfer.ui rename to src/sakia/gui/transfer/transfer.ui index 018f7f3e4603839da12d1cb2a103ae7d7dc18c6c..5c5f691f6201e36bfc52596690c3e0f833e700c1 100644 --- a/res/ui/transfer.ui +++ b/src/sakia/gui/transfer/transfer.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>496</width> - <height>485</height> + <width>800</width> + <height>500</height> </rect> </property> <property name="windowTitle"> @@ -27,151 +27,161 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox"> + <widget class="QGroupBox" name="group_box_recipient"> <property name="title"> <string>Transfer money to</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QRadioButton" name="radio_contact"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Con&tact</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Maximum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QComboBox" name="combo_contact"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QRadioButton" name="radio_pubkey"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>&Recipient public key</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Maximum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLineEdit" name="edit_pubkey"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="inputMask"> - <string/> - </property> - <property name="text"> - <string/> - </property> - <property name="placeholderText"> - <string>Key</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_5"> + <layout class="QVBoxLayout" name="verticalLayout_4"> <property name="topMargin"> <number>6</number> </property> + <property name="bottomMargin"> + <number>6</number> + </property> <item> - <widget class="QRadioButton" name="radio_search"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Search &user</string> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QRadioButton" name="radio_contact"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Con&tact</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QComboBox" name="combo_contact"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> </item> <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Maximum</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="radio_pubkey"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Recipient public key</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLineEdit" name="edit_pubkey"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="inputMask"> + <string/> + </property> + <property name="text"> + <string/> + </property> + <property name="placeholderText"> + <string>Key</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="layout_search_user"> + <property name="topMargin"> + <number>6</number> </property> - </spacer> + <item> + <widget class="QRadioButton" name="radio_search"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Search &user</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Maximum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> </item> </layout> </item> diff --git a/src/sakia/gui/transfer/view.py b/src/sakia/gui/transfer/view.py new file mode 100644 index 0000000000000000000000000000000000000000..327731a1d53803d99cbb615f7967bcc1c8893db2 --- /dev/null +++ b/src/sakia/gui/transfer/view.py @@ -0,0 +1,164 @@ +from PyQt5.QtWidgets import QDialog, QDialogButtonBox +from PyQt5.QtGui import QRegExpValidator +from PyQt5.QtCore import QT_TRANSLATE_NOOP, QRegExp +from .transfer_uic import Ui_TransferMoneyDialog +from enum import Enum +from ..widgets import toast +from ..widgets.dialogs import QAsyncMessageBox + + +class TransferView(QDialog, Ui_TransferMoneyDialog): + """ + Transfer component view + """ + + class ButtonBoxState(Enum): + NO_AMOUNT = 0 + OK = 1 + + class RecipientMode(Enum): + CONTACT = 0 + PUBKEY = 1 + SEARCH = 2 + + _button_box_values = { + ButtonBoxState.NO_AMOUNT: (False, + QT_TRANSLATE_NOOP("TransferView", "No amount. Please give the transfer amount")), + ButtonBoxState.OK: (True, QT_TRANSLATE_NOOP("CertificationView", "&Ok")) + } + + def __init__(self, parent, search_user_view, user_information_view, + communities_names, contacts_names, wallets_names): + """ + + :param parent: + :param sakia.gui.search_user.view.SearchUserView search_user_view: + :param sakia.gui.user_information.view.UserInformationView user_information_view: + :param list[str] communities_names: + :param list[str] contacts_names: + :param list[str] wallets_names: + """ + super().__init__(parent) + self.setupUi(self) + + self.radio_contact.toggled.connect(lambda c, radio=TransferView.RecipientMode.CONTACT: self.recipient_mode_changed(radio)) + self.radio_pubkey.toggled.connect(lambda c, radio=TransferView.RecipientMode.PUBKEY: self.recipient_mode_changed(radio)) + self.radio_search.toggled.connect(lambda c, radio=TransferView.RecipientMode.SEARCH: self.recipient_mode_changed(radio)) + + regexp = QRegExp('^([ a-zA-Z0-9-_:/;*?\[\]\(\)\\\?!^+=@&~#{}|<>%.]{0,255})$') + validator = QRegExpValidator(regexp) + self.edit_message.setValidator(validator) + + for name in communities_names: + self.combo_community.addItem(name) + + for name in sorted(contacts_names): + self.combo_contact.addItem(name) + + for name in wallets_names: + self.combo_wallets.addItem(name) + + if len(contacts_names) == 0: + self.combo_contact.setEnabled(False) + self.radio_pubkey.setChecked(True) + self.radio_contact.setEnabled(False) + + self.search_user = search_user_view + self.user_information_view = user_information_view + + def recipient_mode(self): + if self.radio_contact.isChecked(): + return TransferView.RecipientMode.CONTACT + elif self.radio_search.isChecked(): + return TransferView.RecipientMode.SEARCH + else: + return TransferView.RecipientMode.PUBKEY + + def selected_contact(self): + return self.combo_contact.currentText() + + def set_search_user(self, search_user_view): + """ + + :param sakia.gui.search_user.view.SearchUserView search_user_view: + :return: + """ + self.search_user = search_user_view + self.layout_search_user.addWidget(search_user_view) + self.search_user.button_reset.hide() + + def set_user_information(self, user_information_view): + self.user_information_view = user_information_view + self.group_box_recipient.layout().addWidget(user_information_view) + + def recipient_mode_changed(self, radio): + """ + :param str radio: + """ + self.edit_pubkey.setEnabled(radio == TransferView.RecipientMode.PUBKEY) + self.combo_contact.setEnabled(radio == TransferView.RecipientMode.CONTACT) + self.search_user.setEnabled(radio == TransferView.RecipientMode.SEARCH) + + def change_quantitative_amount(self, amount): + """ + Change relative amount with signals blocked + :param amount: + """ + self.spinbox_amount.blockSignals(True) + self.spinbox_amount.setValue(amount) + self.spinbox_amount.blockSignals(False) + + def change_relative_amount(self, relative): + """ + Change the quantitative amount with signals blocks + :param relative: + """ + self.spinbox_relative.blockSignals(True) + self.spinbox_relative.setValue(relative) + self.spinbox_relative.blockSignals(False) + + def set_spinboxes_parameters(self, tick_quant, max_quant, max_rel): + """ + Configure the spinboxes + It should depend on what the last UD base is + :param int tick_quant: + :param int max_quant: + :param float max_rel: + """ + self.spinbox_amount.setMaximum(max_quant) + self.spinbox_relative.setMaximum(max_rel) + self.spinbox_amount.setSingleStep(tick_quant) + + def refresh_labels(self, total_text, currency): + """ + Refresh displayed texts + :param str total_text: + :param str currency: + """ + self.label_total.setText("{0}".format(total_text)) + self.spinbox_amount.setSuffix(" " + currency) + + def set_button_box(self, state, **kwargs): + """ + Set button box state + :param sakia.gui.transfer.view.TransferView.ButtonBoxState state: the state of te button box + :param dict kwargs: the values to replace from the text in the state + :return: + """ + button_box_state = TransferView._button_box_values[state] + self.button_box.button(QDialogButtonBox.Ok).setEnabled(button_box_state[0]) + self.button_box.button(QDialogButtonBox.Ok).setText(button_box_state[1].format(**kwargs)) + + async def show_success(self, notification, recipient): + if notification: + toast.display(self.tr("Transfer"), + self.tr("Success sending money to {0}").format(recipient)) + else: + await QAsyncMessageBox.information(self.widget, self.tr("Transfer"), + self.tr("Success sending money to {0}").format(recipient)) + + async def show_error(self, notification, error_txt): + if notification: + toast.display(self.tr("Transfer"), "Error : {0}".format(error_txt)) + else: + await QAsyncMessageBox.critical(self.widget, self.tr("Transfer"), error_txt) diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index f2b668fb561f2abdf68ea893bb5d67ef51599e9f..fe34ff8db721241803a13ba2cc38adfbf2b3cfd4 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -5,7 +5,7 @@ import logging from ..user_information.controller import UserInformationController from ..contact import ConfigureContactDialog -from ..transfer import TransferMoneyDialog +from ..transfer.controller import TransferController from ..certification.controller import CertificationController from ...tools.decorators import asyncify from ...core.transfer import Transfer, TransferState @@ -143,7 +143,7 @@ class ContextMenu(QObject): @asyncify async def send_money(self, identity): - await TransferMoneyDialog.send_money_to_identity(self._app, self._account, self._password_asker, + await TransferController.send_money_to_identity(None, self._app, self._account, self._password_asker, self._community, identity) self._app.refresh_transfers.emit() @@ -157,7 +157,7 @@ class ContextMenu(QObject): @asyncify async def send_again(self, transfer): - await TransferMoneyDialog.send_transfer_again(self._app, self._app.current_account, + await TransferController.send_transfer_again(None, self._app, self._app.current_account, self._password_asker, self._community, transfer) self._app.refresh_transfers.emit()