diff --git a/res/icons/icons.qrc b/res/icons/icons.qrc index 1af72a7c804fbb92b62d948d1fdbea1fdbea3d5b..8fe92ad92db25a2c85261a719b8b1844ae52af9f 100644 --- a/res/icons/icons.qrc +++ b/res/icons/icons.qrc @@ -1,5 +1,7 @@ <RCC> <qresource prefix="icons"> + <file alias="leave_icon">noun_155520_cc.svg</file> + <file alias="new_membership">noun_155540_cc.svg</file> <file alias="payment_icon">noun_178785_cc.svg</file> <file alias="renew_membership">noun_155533_cc.svg</file> <file alias="certification_icon">noun_41979_cc.svg</file> diff --git a/res/ui/community_cfg.ui b/res/ui/community_cfg.ui index 0d18585d3a33202199ab2f5ae604aa3032e3099a..408cf47d40b91f5bc117fb7a2ed3ec55f1ca4752 100644 --- a/res/ui/community_cfg.ui +++ b/res/ui/community_cfg.ui @@ -25,7 +25,7 @@ <property name="currentIndex"> <number>0</number> </property> - <widget class="QWidget" name="page_init"> + <widget class="QWidget" name="page_node"> <layout class="QVBoxLayout" name="verticalLayout_4"> <item> <spacer name="verticalSpacer_2"> @@ -75,45 +75,66 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_5"> <property name="topMargin"> <number>6</number> </property> <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <widget class="QPushButton" name="button_register"> + <property name="text"> + <string>Register your account</string> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/new_membership</normaloff>:/icons/new_membership</iconset> + </property> + <property name="iconSize"> + <size> + <width>32</width> + <height>32</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button_connect"> + <property name="text"> + <string>Connect using your account</string> + </property> + <property name="icon"> + <iconset resource="../icons/icons.qrc"> + <normaloff>:/icons/connect_icon</normaloff>:/icons/connect_icon</iconset> </property> - <property name="sizeHint" stdset="0"> + <property name="iconSize"> <size> - <width>40</width> - <height>20</height> + <width>32</width> + <height>32</height> </size> </property> - </spacer> + </widget> </item> <item> - <widget class="QPushButton" name="button_checknode"> + <widget class="QLabel" name="label_error"> <property name="text"> - <string>Check node connectivity</string> + <string/> </property> </widget> </item> </layout> </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> </layout> </widget> <widget class="QWidget" name="page_add_nodes"> @@ -173,7 +194,7 @@ </widget> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> + <layout class="QHBoxLayout" name="layout_previous_next"> <item> <widget class="QPushButton" name="button_previous"> <property name="enabled"> @@ -211,7 +232,9 @@ </item> </layout> </widget> - <resources/> + <resources> + <include location="../icons/icons.qrc"/> + </resources> <connections> <connection> <sender>button_add</sender> diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index 3491a551b163d789f0117d084bb77af855202e41..ac3c6ac9776534fd5eed999d590eb3678c4914ea 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -300,7 +300,7 @@ class Account(QObject): logging.debug("Key publish : {0}".format(selfcert.signed_raw())) replies = community.bma_access.broadcast(qtbma.wot.Add, {}, {'pubkey': self.pubkey, 'self_': selfcert.signed_raw(), - 'other': []}) + 'other': ""}) for r in replies: r.finished.connect(lambda reply=r: self.__handle_selfcert_replies(replies, reply)) diff --git a/src/cutecoin/gui/password_asker.py b/src/cutecoin/gui/password_asker.py index 30815fe826d8901887e4d6a60ff921615545e262..04a7a4f92b9e262bbaafc47500f3da5ea492ded3 100644 --- a/src/cutecoin/gui/password_asker.py +++ b/src/cutecoin/gui/password_asker.py @@ -6,7 +6,7 @@ Created on 24 dec. 2014 import logging import re - +import asyncio from PyQt5.QtCore import QEvent from PyQt5.QtWidgets import QDialog, QMessageBox @@ -29,6 +29,21 @@ class PasswordAskerDialog(QDialog, Ui_PasswordAskerDialog): self.password = "" self.remember = False + def future_exec(self): + future = asyncio.Future() + if not self.remember: + def future_show(): + pwd = self.password + if not self.remember: + self.password = "" + future.set_result(pwd) + self.open() + self.finished.connect(future_show) + else: + self.setResult(QDialog.Accepted) + future.set_result(self.password) + return future + def exec_(self): if not self.remember: super().exec_() diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py index fa1b2a715963fd35c817700712e9cffbaec0605f..73e8415fcc630207a298be4c820bad1f2b92b767 100644 --- a/src/cutecoin/gui/process_cfg_community.py +++ b/src/cutecoin/gui/process_cfg_community.py @@ -9,7 +9,7 @@ import asyncio from PyQt5.QtWidgets import QDialog, QMenu, QMessageBox, QApplication from PyQt5.QtGui import QCursor -from PyQt5.QtCore import pyqtSlot, pyqtSignal +from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from ..gen_resources.community_cfg_uic import Ui_CommunityConfigurationDialog from ..models.peering import PeeringTreeModel @@ -19,8 +19,9 @@ from ..core.net import Node from . import toast -class Step(): +class Step(QObject): def __init__(self, config_dialog, previous_step=None, next_step=None): + super().__init__() self.previous_step = previous_step self.next_step = next_step self.config_dialog = config_dialog @@ -34,26 +35,93 @@ class StepPageInit(Step): super().__init__(config_dialog) self.node = None logging.debug("Init") - self.config_dialog.button_next.setEnabled(False) - self.config_dialog.button_checknode.clicked.connect(self.check_node) + self.config_dialog.button_connect.clicked.connect(self.check_connect) + self.config_dialog.button_register.clicked.connect(self.check_register) + + @property + def app(self): + return self.config_dialog.app + + @property + def account(self): + return self.config_dialog.account + + @property + def community(self): + return self.config_dialog.community + + @property + def password_asker(self): + return self.config_dialog.password_asker + + @asyncio.coroutine + def coroutine_check_connect(self): + server = self.config_dialog.lineedit_server.text() + port = self.config_dialog.spinbox_port.value() + logging.debug("Is valid ? ") + self.node = yield from Node.from_address(self.config_dialog.app.network_manager, None, server, port) + if self.node: + identity = yield from self.app.identities_registry.future_find(self.account.pubkey, self.community) + if identity.blockchain_state == BlockchainState.NOT_FOUND: + self.config_dialog.label_error.setText(self.tr("Could not find your identity on the network.")) + else: + self.next() + + @pyqtSlot() + def check_connect(self): + logging.debug("Check node") + asyncio.async(self.coroutine_check_connect()) @asyncio.coroutine - def coroutine_check_node(self): + def coroutine_check_register(self): server = self.config_dialog.lineedit_server.text() port = self.config_dialog.spinbox_port.value() logging.debug("Is valid ? ") self.node = yield from Node.from_address(self.config_dialog.app.network_manager, None, server, port) if self.node: - self.config_dialog.button_next.setEnabled(True) - self.config_dialog.button_checknode.setText("Ok !") + community = Community.create(self.app.network_manager, self.node) + identity = yield from self.app.identities_registry.future_find(self.account.pubkey, community) + if identity.blockchain_state == BlockchainState.NOT_FOUND: + password = yield from self.password_asker.future_exec() + if self.password_asker.result() == QDialog.Rejected: + return + self.config_dialog.label_error.setText(self.tr("Broadcasting identity...")) + self.account.selfcert_broadcasted.connect(self.handle_broadcast) + self.account.broadcast_error.connect(self.handle_error) + yield from self.account.send_selfcert(password, community) + self.config_dialog.community = community else: - self.config_dialog.button_next.setEnabled(False) - self.config_dialog.button_checknode.setText("Could not connect.") + self.config_dialog.label_error.setText(self.tr("Could not connect.")) @pyqtSlot() - def check_node(self): + def check_register(self): logging.debug("Check node") - asyncio.async(self.coroutine_check_node()) + asyncio.async(self.coroutine_check_register()) + + @pyqtSlot(int, str) + def handle_broadcast(self): + if self.app.preferences['notifications']: + toast.display(self.tr("UID broadcast"), self.tr("Identity broadcasted to the network")) + # Disabled : https://github.com/harvimt/quamash/issues/41 + # else: + # QMessageBox.information(self, self.tr("UID broadcast"), self.tr("Identity broadcasted to the network")) + self.account.selfcert_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() + self.config_dialog.next() + + @pyqtSlot(int, str) + def handle_error(self, error_code, text): + self.config_dialog.label_error.setText(self.tr("Error") + " " + \ + self.tr("{0} : {1}".format(error_code, text))) + if self.app.preferences['notifications']: + toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + # Disabled : https://github.com/harvimt/quamash/issues/41 + # else: + # QMessageBox.critical(self, self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) + self.account.selfcert_broadcasted.disconnect() + self.account.broadcast_error.disconnect(self.handle_error) + QApplication.restoreOverrideCursor() def is_valid(self): return self.node is not None @@ -67,7 +135,8 @@ class StepPageInit(Step): self.config_dialog.community = Community.create(self.config_dialog.app.network_manager, self.node) def display_page(self): - self.config_dialog.button_previous.setEnabled(False) + self.config_dialog.button_next.hide() + self.config_dialog.button_previous.hide() class StepPageAddpeers(Step): @@ -84,6 +153,8 @@ class StepPageAddpeers(Step): pass def display_page(self): + self.config_dialog.button_next.show() + self.config_dialog.button_previous.show() # We add already known peers to the displayed list self.config_dialog.nodes = self.config_dialog.community.network.root_nodes tree_model = PeeringTreeModel(self.config_dialog.community) @@ -98,7 +169,6 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): Dialog to configure or add a community """ community_added = pyqtSignal() - pubkey_not_found = pyqtSignal() def __init__(self, app, account, community, password_asker): """ @@ -119,7 +189,6 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): self.nodes = [] self.community_added.connect(self.add_community_and_close) - self.pubkey_not_found.connect(self.question_publish_pubkey) step_init = StepPageInit(self) step_add_peers = StepPageAddpeers(self) @@ -144,7 +213,7 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): self.stacked_pages.setCurrentIndex(next_index) self.step.display_page() else: - asyncio.async(self.final()) + self.add_community_and_close() def previous(self): if self.step.previous_step is not None: @@ -201,64 +270,14 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): action.setEnabled(False) menu.exec_(QCursor.pos()) - def selfcert_sent(self, pubkey, currency): - if self.app.preferences['notifications']: - toast.display(self.tr("UID Publishing"), - self.tr("Success publishing your UID").format(pubkey, currency)) - else: - QMessageBox.information(self, self.tr("UID Publishing"), - self.tr("Success publishing your UID").format(pubkey, currency)) - self.account.certification_broadcasted.disconnect() - self.account.broadcast_error.disconnect(self.handle_error) - QApplication.restoreOverrideCursor() - self.add_community_and_close() - - @pyqtSlot(int, str) - def handle_broadcast(self, error_code, text): - if self.app.preferences['notifications']: - toast.display(self.tr("UID broadcast"), self.tr("Identity broadcasted to the network")) - else: - QMessageBox.information(self, self.tr("UID broadcast"), self.tr("Identity broadcasted to the network")) - self.account.certification_broadcasted.disconnect() - self.account.broadcast_error.disconnect(self.handle_error) - QApplication.restoreOverrideCursor() - - @pyqtSlot(int, str) - def handle_error(self, error_code, text): - if self.app.preferences['notifications']: - toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) - else: - QMessageBox.critical(self, self.tr("Error"), self.tr("{0} : {1}".format(error_code, text))) - self.account.selfcert_broadcasted.disconnect() - self.account.broadcast_error.disconnect(self.handle_error) - QApplication.restoreOverrideCursor() + @asyncio.coroutine + def async_exec(self): + future = asyncio.Future() + self.finished.connect(lambda r: future.set_result(r)) + self.open() + return future - @pyqtSlot() def add_community_and_close(self): if self.community not in self.account.communities: self.account.add_community(self.community) self.accept() - - @pyqtSlot() - def question_publish_pubkey(self): - reply = QMessageBox.question(self, self.tr("Pubkey not found"), - self.tr("""The public key of your account wasn't found in the community. :\n -{0}\n -Would you like to publish the key ?""").format(self.account.pubkey)) - if reply == QMessageBox.Yes: - password = self.password_asker.exec_() - if self.password_asker.result() == QDialog.Rejected: - return - self.account.selfcert_broadcasted.connect(self.handle_broadcast) - self.account.broadcast_error.connect(self.handle_error) - asyncio.async(self.account.send_selfcert(password, self.community)) - else: - self.community_added.emit() - - @asyncio.coroutine - def final(self): - identity = yield from self.app.identities_registry.future_find(self.account.pubkey, self.community) - if identity.blockchain_state == BlockchainState.NOT_FOUND: - self.pubkey_not_found.emit() - else: - self.community_added.emit() diff --git a/src/cutecoin/tests/mocks/bma/new_blockchain.py b/src/cutecoin/tests/mocks/bma/new_blockchain.py index ca38d64eaa23317dd6c69f9a3a7cbe2efedeff58..71f2932b1d144116acb09a7d70d2500b610b85f3 100644 --- a/src/cutecoin/tests/mocks/bma/new_blockchain.py +++ b/src/cutecoin/tests/mocks/bma/new_blockchain.py @@ -61,7 +61,7 @@ def get_mock(): times=1, headers={'Content-Type': 'application/json'}) - mock.when('POST wot/add')\ + mock.when('POST /wot/add.*')\ .reply(body=bma_wot_add, status=200, times=FOREVER, diff --git a/src/cutecoin/tests/process_cfg_community/test_add_community.py b/src/cutecoin/tests/process_cfg_community/test_add_community.py index 5a866f705bc344524d678df24dee43446adb4562..1e7d74dbd7de708a01951c2c86959dd540ee005a 100644 --- a/src/cutecoin/tests/process_cfg_community/test_add_community.py +++ b/src/cutecoin/tests/process_cfg_community/test_add_community.py @@ -42,35 +42,46 @@ class ProcessAddCommunity(unittest.TestCase): asyncio.set_event_loop(None) def test_add_community_empty_blockchain(self): - self.process_community = ProcessConfigureCommunity(self.application, self.account, None, self.password_asker) + process_community = ProcessConfigureCommunity(self.application, + self.account, + None, self.password_asker) + @asyncio.coroutine + def open_dialog(): + result = yield from process_community.async_exec() @asyncio.coroutine def exec_test(): mock = new_blockchain.get_mock() logging.debug(mock.pretend_url) + asyncio.async(open_dialog()) + yield from asyncio.sleep(1) self.network_manager.set_mock_path(mock.pretend_url) - QTest.mouseClick(self.process_community.lineedit_server, Qt.LeftButton) - QTest.keyClicks(self.process_community.lineedit_server, "127.0.0.1") - QTest.mouseDClick(self.process_community.spinbox_port, Qt.LeftButton) - self.process_community.spinbox_port.setValue(50000) - self.assertEqual(self.process_community.stacked_pages.currentWidget(), self.process_community.page_init) - self.assertEqual(self.process_community.lineedit_server.text(), "127.0.0.1") - self.assertEqual(self.process_community.spinbox_port.value(), 50000) - QTest.mouseClick(self.process_community.button_checknode, Qt.LeftButton) - yield from asyncio.sleep(3) - self.assertEqual(self.process_community.button_checknode.text(), "Ok !") + QTest.mouseClick(process_community.lineedit_server, Qt.LeftButton) + QTest.keyClicks(process_community.lineedit_server, "127.0.0.1") + QTest.mouseDClick(process_community.spinbox_port, Qt.LeftButton) + process_community.spinbox_port.setValue(50000) + self.assertEqual(process_community.stacked_pages.currentWidget(), + process_community.page_node) + self.assertEqual(process_community.lineedit_server.text(), "127.0.0.1") + self.assertEqual(process_community.spinbox_port.value(), 50000) + QTest.mouseClick(process_community.button_register, Qt.LeftButton) + yield from asyncio.sleep(1) self.assertEqual(mock.get_request(0).method, 'GET') self.assertEqual(mock.get_request(0).url, '/network/peering') - QTest.mouseClick(self.process_community.button_next, Qt.LeftButton) - self.assertEqual(self.process_community.stacked_pages.currentWidget(), self.process_community.page_add_nodes) - #QTest.mouseClick(self.process_community.button_next, Qt.LeftButton) - #yield from asyncio.sleep(3) + self.assertEqual(mock.get_request(1).method, 'GET') + self.assertEqual(mock.get_request(1).url, + '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') + for i in range(2, 5): + self.assertEqual(mock.get_request(i).method, 'GET') + self.assertEqual(mock.get_request(i).url, + '/wot/lookup/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') + + self.assertEqual(mock.get_request(6).method, 'POST') + self.assertEqual(mock.get_request(6).url[:8], '/wot/add') + self.assertEqual(process_community.label_error.text(), "Broadcasting identity...") - # There is a bug here, it should not request certifiers-of 3 times in a row - #self.assertEqual(mock.get_request(1).method, 'GET') - #self.assertEqual(mock.get_request(1).url, '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') - #self.assertEqual(mock.get_request(2).method, 'GET') - #self.assertEqual(mock.get_request(2).url, '/wot/lookup/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') + self.assertEqual(process_community.stacked_pages.currentWidget(), + process_community.page_add_nodes) self.lp.run_until_complete(asyncio.wait_for(exec_test(), timeout=10))