From fda6e5605adde3597ae6009cb9e363f11a5f7260 Mon Sep 17 00:00:00 2001 From: navjot Date: Thu, 12 Dec 2019 20:08:07 +0530 Subject: [PATCH] Kivy code update part 1 --- src/bitmessagekivy/kivy_helper_search.py | 24 +- src/bitmessagekivy/main.kv | 276 ++-- src/bitmessagekivy/mpybit.py | 1901 ++++++++++++++-------- src/images/loader.zip | Bin 0 -> 15918 bytes src/state.py | 4 + 5 files changed, 1433 insertions(+), 772 deletions(-) create mode 100644 src/images/loader.zip diff --git a/src/bitmessagekivy/kivy_helper_search.py b/src/bitmessagekivy/kivy_helper_search.py index a1fe4a99..48e77cb2 100644 --- a/src/bitmessagekivy/kivy_helper_search.py +++ b/src/bitmessagekivy/kivy_helper_search.py @@ -1,11 +1,10 @@ """ -src/bitmessagekivy/kivy_helper_search.py -================================= +Sql queries for bitmessagekivy """ from helper_sql import sqlQuery -def search_sql(xAddress="toaddress", account=None, folder="inbox", where=None, what=None, unreadOnly=False): +def search_sql(xAddress="toaddress", account=None, folder="inbox", where=None, what=None, unreadOnly=False, start_indx=0, end_indx=20): """Method helping for searching mails""" # pylint: disable=too-many-arguments, too-many-branches if what is not None and what != "": @@ -14,14 +13,15 @@ def search_sql(xAddress="toaddress", account=None, folder="inbox", where=None, w what = None if folder == "sent" or folder == "draft": - sqlStatementBase = ''' - SELECT toaddress, fromaddress, subject, message, status, ackdata, lastactiontime - FROM sent ''' + sqlStatementBase = ( + '''SELECT toaddress, fromaddress, subject, message, status, ackdata,''' + ''' lastactiontime FROM sent ''') elif folder == "addressbook": sqlStatementBase = '''SELECT label, address From addressbook ''' else: - sqlStatementBase = '''SELECT folder, msgid, toaddress, message, fromaddress, subject, received, read - FROM inbox ''' + sqlStatementBase = ( + '''SELECT folder, msgid, toaddress, message, fromaddress, subject,''' + ''' received, read FROM inbox ''') sqlStatementParts = [] sqlArguments = [] @@ -58,8 +58,10 @@ def search_sql(xAddress="toaddress", account=None, folder="inbox", where=None, w sqlStatementParts.append("read = 0") if sqlStatementParts: sqlStatementBase += "WHERE " + " AND ".join(sqlStatementParts) - if folder == "sent": - sqlStatementBase += " ORDER BY lastactiontime DESC" + if folder == "sent" or folder == "draft": + sqlStatementBase += " ORDER BY lastactiontime DESC limit {0}, {1}".format(start_indx, end_indx) elif folder == "inbox": - sqlStatementBase += " ORDER BY received DESC" + sqlStatementBase += " ORDER BY received DESC limit {0}, {1}".format(start_indx, end_indx) + # elif folder == "addressbook": + # sqlStatementBase += " limit {0}, {1}".format(start_indx, end_indx) return sqlQuery(sqlStatementBase, sqlArguments) diff --git a/src/bitmessagekivy/main.kv b/src/bitmessagekivy/main.kv index 3afc794b..b360a534 100644 --- a/src/bitmessagekivy/main.kv +++ b/src/bitmessagekivy/main.kv @@ -1,38 +1,21 @@ #:import MDToolbar kivymd.uix.toolbar.MDToolbar -#:import ThemeManager kivymd.theming.ThemeManager -#:import MDNavigationDrawer kivymd.uix.navigationdrawer.MDNavigationDrawer #:import NavigationLayout kivymd.uix.navigationdrawer.NavigationLayout #:import NavigationDrawerDivider kivymd.uix.navigationdrawer.NavigationDrawerDivider -#:import NavigationDrawerToolbar kivymd.uix.navigationdrawer.NavigationDrawerToolbar #:import NavigationDrawerSubheader kivymd.uix.navigationdrawer.NavigationDrawerSubheader #:import MDCheckbox kivymd.uix.selectioncontrol.MDCheckbox -#:import MDSwitch kivymd.uix.selectioncontrol.MDSwitch #:import MDList kivymd.uix.list.MDList #:import OneLineListItem kivymd.uix.list.OneLineListItem -#:import TwoLineListItem kivymd.uix.list.TwoLineListItem -#:import ThreeLineListItem kivymd.uix.list.ThreeLineListItem -#:import OneLineAvatarListItem kivymd.uix.list.OneLineAvatarListItem -#:import OneLineIconListItem kivymd.uix.list.OneLineIconListItem -#:import OneLineAvatarIconListItem kivymd.uix.list.OneLineAvatarIconListItem #:import MDTextField kivymd.uix.textfield.MDTextField -#:import MDSpinner kivymd.uix.spinner.MDSpinner -#:import MDCard kivymd.uix.card.MDCard -#:import MDSeparator kivymd.uix.card.MDSeparator -#:import MDDropdownMenu kivymd.uix.menu.MDDropdownMenu #:import get_color_from_hex kivy.utils.get_color_from_hex +#:import MDCard kivymd.uix.card.MDCard #:import colors kivymd.color_definitions.colors -#:import MDSlider kivymd.uix.slider.MDSlider #:import MDTabs kivymd.uix.tab.MDTabs -#:import MDProgressBar kivymd.uix.progressbar.MDProgressBar -#:import MDAccordion kivymd.uix.accordion.MDAccordion -#:import MDAccordionItem kivymd.uix.accordion.MDAccordionItem -#:import MDAccordionSubItem kivymd.uix.accordion.MDAccordionSubItem #:import MDFloatingActionButton kivymd.uix.button.MDFloatingActionButton #:import Factory kivy.factory.Factory -#:import MDTextButton kivymd.uix.button.MDTextButton -#:import FadeTransition kivy.uix.screenmanager.FadeTransition #:import MDScrollViewRefreshLayout kivymd.uix.refreshlayout.MDScrollViewRefreshLayout +#:import MDSpinner kivymd.uix.spinner.MDSpinner +#:import NoTransition kivy.uix.screenmanager.NoTransition #:import MDTabsBase kivymd.uix.tab.MDTabsBase @@ -51,8 +34,9 @@ color: color_font : - drawer_logo: app.address_identicon() + drawer_logo: './images/drawer_logo1.png' NavigationDrawerDivider: + height: dp(7) NavigationDrawerSubheader: text: "Accounts" NavigationDrawerIconButton: @@ -79,52 +63,47 @@ text: "Inbox" on_release: app.root.ids.scr_mngr.current = 'inbox' badge_text: "0" - on_press: app.refreshScreen(self) + on_press: app.load_screen(self) NavigationDrawerIconButton: id: send_cnt icon: 'send' text: "Sent" on_release: app.root.ids.scr_mngr.current = 'sent' badge_text: "0" - on_press: app.refreshScreen(self) NavigationDrawerIconButton: id: draft_cnt icon: 'message-draw' text: "Draft" on_release: app.root.ids.scr_mngr.current = 'draft' badge_text: "0" - on_press: app.refreshScreen(self) - NavigationDrawerIconButton: - text: "Starred" - icon:'star' - on_release: app.root.ids.scr_mngr.current = 'starred' - on_press: app.refreshScreen(self) - NavigationDrawerIconButton: - icon: 'archive' - text: "Archieve" - on_release: app.root.ids.scr_mngr.current = 'archieve' - badge_text: "0" - on_press: app.refreshScreen(self) - NavigationDrawerIconButton: - icon: 'email-open-outline' - text: "Spam" - on_release: app.root.ids.scr_mngr.current = 'spam' - badge_text: "0" - on_press: app.refreshScreen(self) + #NavigationDrawerIconButton: + #text: "Starred" + #icon:'star' + #on_release: app.root.ids.scr_mngr.current = 'starred' + #badge_text: "0" + #NavigationDrawerIconButton: + #icon: 'archive' + #text: "Archieve" + #on_release: app.root.ids.scr_mngr.current = 'archieve' + #badge_text: "0" + #NavigationDrawerIconButton: + #icon: 'email-open-outline' + #text: "Spam" + #on_release: app.root.ids.scr_mngr.current = 'spam' + #badge_text: "0" NavigationDrawerIconButton: id: trash_cnt icon: 'delete' text: "Trash" on_release: app.root.ids.scr_mngr.current = 'trash' badge_text: "0" - on_press: app.refreshScreen(self) NavigationDrawerIconButton: id: allmail_cnt text: "All Mails" icon:'contact-mail' on_release: app.root.ids.scr_mngr.current = 'allmails' badge_text: "0" - on_press: app.refreshScreen(self) + on_press: app.load_screen(self) NavigationDrawerDivider: NavigationDrawerSubheader: text: "All labels" @@ -132,37 +111,30 @@ text: "Address Book" icon:'book-multiple' on_release: app.root.ids.scr_mngr.current = 'addressbook' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "Settings" icon:'settings' on_release: app.root.ids.scr_mngr.current = 'set' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "Subscriptions/Payment" icon:'bell' on_release: app.root.ids.scr_mngr.current = 'payment' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "Credits" icon:'wallet' on_release: app.root.ids.scr_mngr.current = 'credits' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "new address" icon:'account-plus' on_release: app.root.ids.scr_mngr.current = 'login' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "Network Status" icon:'server-network' on_release: app.root.ids.scr_mngr.current = 'networkstat' - on_press: app.refreshScreen(self) NavigationDrawerIconButton: text: "My Addresses" icon:'account-multiple' on_release: app.root.ids.scr_mngr.current = 'myaddress' - on_press: app.refreshScreen(self) NavigationLayout: id: nav_layout @@ -170,78 +142,87 @@ NavigationLayout: ContentNavigationDrawer: id: nav_drawer - BoxLayout: - id: box_layout - orientation: 'vertical' - MDToolbar: - id: toolbar - title: app.current_address_label() - opacity: 1 if app.addressexist() else 0 - disabled: False if app.addressexist() else True - md_bg_color: app.theme_cls.primary_color - background_palette: 'Primary' - background_hue: '500' - left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]] - right_action_items: [['account-plus', lambda x: app.addingtoaddressbook()]] + FloatLayout: + id: float_box + BoxLayout: + id: box_layout + orientation: 'vertical' + MDToolbar: + id: toolbar + title: app.current_address_label() + opacity: 1 if app.addressexist() else 0 + disabled: False if app.addressexist() else True + md_bg_color: app.theme_cls.primary_color + background_palette: 'Primary' + background_hue: '500' + left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]] + right_action_items: [['account-plus', lambda x: app.addingtoaddressbook()]] - ScreenManager: - id: scr_mngr - Inbox: - id:sc1 - Page: - id:sc2 - Create: - id:sc3 - Sent: - id:sc4 - Trash: - id:sc5 - Login: - id:sc6 - Random: - id:sc7 - AddressSuccessful: - id:sc8 - Setting: - id:sc9 - MyAddress: - id:sc10 - AddressBook: - id:sc11 - Payment: - id:sc12 - NetworkStat: - id:sc13 - MailDetail: - id:sc14 - ShowQRCode: - id:sc15 - Draft: - id:sc16 - Allmails: - id:sc17 - Credits: - id:sc18 - Starred: - id:sc19 - Archieve: - id:sc20 - Spam: - id:sc21 + ScreenManager: + id: scr_mngr + Inbox: + id:sc1 + Page: + id:sc2 + Create: + id:sc3 + Sent: + id:sc4 + Trash: + id:sc5 + Login: + id:sc6 + Random: + id:sc7 + Spam: + id:sc8 + Setting: + id:sc9 + MyAddress: + id:sc10 + AddressBook: + id:sc11 + Payment: + id:sc12 + NetworkStat: + id:sc13 + MailDetail: + id:sc14 + ShowQRCode: + id:sc15 + Draft: + id:sc16 + Allmails: + id:sc17 + Credits: + id:sc18 + Starred: + id:sc19 + Archieve: + id:sc20 : name: 'inbox' + transition: NoTransition() BoxLayout: orientation: 'vertical' spacing: dp(10) SearchBar: - FloatLayout: - MDScrollViewRefreshLayout: - id: refresh_layout - refresh_callback: root.refresh_callback - root_layout: root + #FloatLayout: + # MDScrollViewRefreshLayout: + # id: refresh_layout + # refresh_callback: root.refresh_callback + # root_layout: root.set_root_layout() + # MDList: + # id: ml + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False MDList: id: ml + Loader: ComposerButton: : @@ -252,22 +233,27 @@ NavigationLayout: BoxLayout: orientation:'vertical' ScrollView: + id: scroll_y do_scroll_x: False MDList: id: ml + Loader: ComposerButton: : name: 'trash' ScrollView: + id: scroll_y do_scroll_x: False MDList: id: ml + Loader: ComposerButton: : name: 'draft' ScrollView: + id: scroll_y do_scroll_x: False MDList: id: ml @@ -299,13 +285,21 @@ NavigationLayout: : name: 'allmails' - FloatLayout: - MDScrollViewRefreshLayout: - id: refresh_layout - refresh_callback: root.refresh_callback - root_layout: root + #FloatLayout: + # MDScrollViewRefreshLayout: + # id: refresh_layout + # refresh_callback: root.refresh_callback + # root_layout: root.set_root_layout() + # MDList: + # id: ml + BoxLayout: + orientation:'vertical' + ScrollView: + id: scroll_y + do_scroll_x: False MDList: id: ml + Loader: ComposerButton: : @@ -322,6 +316,7 @@ NavigationLayout: : name: 'create' + Loader: : name: 'credits' @@ -389,7 +384,7 @@ NavigationLayout: size_hint_y: None font_size: '13sp' height: self.parent.height/2 - hint_text: 'type or search recipients address starting with BM-' + hint_text: 'type, select or scan QR code for recipients address' RV: id: rv MDTextField: @@ -499,6 +494,7 @@ NavigationLayout: MDRaisedButton: height: dp(40) on_press: app.root.ids.scr_mngr.current = 'random' + on_press: app.root.ids.sc7.reset_address_label() MDLabel: font_style: 'H6' text: 'proceed' @@ -535,6 +531,7 @@ NavigationLayout: hint_text: "Label" required: True helper_text_mode: "on_error" + on_text: root.add_validation(self) BoxLayout: AnchorLayout: MDRaisedButton: @@ -632,9 +629,10 @@ NavigationLayout: MDScrollViewRefreshLayout: id: refresh_layout refresh_callback: root.refresh_callback - root_layout: root + root_layout: root.set_root_layout() MDList: id: ml + Loader: ComposerButton: : @@ -645,9 +643,11 @@ NavigationLayout: BoxLayout: orientation:'vertical' ScrollView: + id: scroll_y do_scroll_x: False MDList: id: ml + Loader: ComposerButton: : @@ -656,7 +656,7 @@ NavigationLayout: do_scroll_x: False BoxLayout: orientation: 'vertical' - padding: [dp(app.window_size[0]/16 if app.window_size[0] <= 720 else app.window_size[0]/4*1.1), dp(10)] + padding: [dp(app.window_size[0]/16 if app.window_size[0] <= 720 else app.window_size[0]/6 if app.window_size[0] <= 800 else app.window_size[0]/18), dp(10)] spacing: 12 size_hint_y: None height: self.minimum_height + dp(app.window_size[1]) if app.window_size[1] > app.window_size[0] else dp(app.window_size[0]) @@ -820,6 +820,7 @@ NavigationLayout: hint_text: "Label" required: True helper_text_mode: "on_error" + on_text: root.checkLabel_valid(self) MDTextField: id: address hint_text: "Address" @@ -968,24 +969,24 @@ NavigationLayout: font_size: '20sp' CopyTextBtn: MDLabel: - font_style: 'H4' + font_style: 'Body1' theme_text_color: 'Primary' text: "From: " + root.from_addr halign: 'left' CopyTextBtn: MDLabel: - font_style: 'H4' + font_style: 'Body1' theme_text_color: 'Primary' text: "To: " + root.to_addr halign: 'left' CopyTextBtn: MDLabel: - font_style: 'H4' + font_style: 'Body1' theme_text_color: 'Primary' text: root.status halign: 'left' MDLabel: - font_style: 'H4' + font_style: 'Subtitle2' theme_text_color: 'Primary' text: root.message halign: 'left' @@ -995,6 +996,7 @@ NavigationLayout: orientation: 'vertical' size_hint_y: None height: dp(100) + self.minimum_height + Loader: : id: cpyButton @@ -1137,6 +1139,7 @@ NavigationLayout: theme_text_color: 'Primary' required: True helper_text_mode: "on_error" + on_text: root.checkLabel_valid(self) MDLabel: font_style: 'Subtitle2' theme_text_color: 'Primary' @@ -1144,6 +1147,7 @@ NavigationLayout: font_size: '17sp' halign: 'left' MDLabel: + id: address font_style: 'Body1' theme_text_color: 'Primary' text: root.address @@ -1211,4 +1215,26 @@ NavigationLayout: MDTextField: id: search_field hint_text: 'Search' - on_text: app.searchQuery(self) \ No newline at end of file + on_text: app.searchQuery(self) + + +: + separator_color: 1, 1, 1, 1 + background: "White.png" + Button: + id: btn + disabled: True + background_disabled_normal: "White.png" + Image: + source: './images/loader.zip' + anim_delay: 0 + #mipmap: True + size: root.size + + +: + id: spinner + size_hint: None, None + size: dp(46), dp(46) + pos_hint: {'center_x': 0.5, 'center_y': 0.5} + active: False \ No newline at end of file diff --git a/src/bitmessagekivy/mpybit.py b/src/bitmessagekivy/mpybit.py index 5c41577e..1a0d13f2 100644 --- a/src/bitmessagekivy/mpybit.py +++ b/src/bitmessagekivy/mpybit.py @@ -5,8 +5,8 @@ src/bitmessagekivy/mpybit.py # pylint: disable=relative-import, unused-variable, import-error, no-name-in-module, too-many-lines import os import time -from functools import partial from bmconfigparser import BMConfigParser +from functools import partial from helper_sql import sqlExecute, sqlQuery from kivy.app import App from kivy.clock import Clock @@ -19,7 +19,8 @@ from kivy.properties import ( ListProperty, NumericProperty, ObjectProperty, - StringProperty) + StringProperty +) from kivy.uix.behaviors import FocusBehavior from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button @@ -44,7 +45,8 @@ from kivymd.uix.list import ( ILeftBodyTouch, IRightBodyTouch, TwoLineAvatarIconListItem, - TwoLineListItem) + TwoLineListItem +) from kivymd.uix.navigationdrawer import ( MDNavigationDrawer, NavigationDrawerHeaderBase) @@ -58,6 +60,7 @@ import state from bitmessagekivy.uikivysignaler import UIkivySignaler from bitmessagekivy import identiconGeneration +from addresses import addBMIfNotPresent, decodeAddress, encodeVarint # pylint: disable=unused-argument, too-few-public-methods @@ -78,80 +81,54 @@ class Navigatorss(MDNavigationDrawer): class Inbox(Screen): """Inbox Screen uses screen to show widgets of screens.""" + queryreturn = ListProperty() + has_refreshed = True + account = StringProperty() def __init__(self, *args, **kwargs): """Method Parsing the address.""" super(Inbox, self).__init__(*args, **kwargs) + Clock.schedule_once(self.init_ui, 0) + + @staticmethod + def set_defaultAddress(): + """This method set default address""" if state.association == '': if BMConfigParser().addresses(): state.association = BMConfigParser().addresses()[0] - Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method inbox accounts.""" - self.inboxaccounts() - print(dt) + """Clock schdule for method inbox accounts.""" + self.loadMessagelist() - def inboxaccounts(self): - """Load inbox accounts.""" - account = state.association - self.loadMessagelist(account, 'All', '') - - def loadMessagelist(self, account, where="", what=""): + def loadMessagelist(self, where="", what=""): """Load Inbox list for Inbox messages.""" # pylint: disable=too-many-locals + self.set_defaultAddress() + self.account = state.association if state.searcing_text: + self.children[2].children[0].children[0].scroll_y = 1.0 where = ['subject', 'message'] what = state.searcing_text xAddress = 'toaddress' data = [] - queryreturn = kivy_helper_search.search_sql( - xAddress, account, "inbox", where, what, False) - if queryreturn: + self.inboxDataQuery(xAddress, where, what) + if self.queryreturn: + state.kivyapp.get_inbox_count() src_mng_obj = state.kivyapp.root.children[2].children[0].ids - src_mng_obj.inbox_cnt.badge_text = str(len(queryreturn)) - state.inbox_count = str(len(queryreturn)) - state.kivyapp.root.ids.sc17.clear_widgets() - state.kivyapp.root.ids.sc17.add_widget(Allmails()) - for mail in queryreturn: - third_text = mail[3].replace('\n', ' ') + src_mng_obj.inbox_cnt.badge_text = state.inbox_count + for mail in self.queryreturn: + # third_text = mail[3].replace('\n', ' ') data.append({ 'text': mail[4].strip(), 'secondary_text': mail[5][:50] + '........' if len( - mail[5]) >= 50 else ( - mail[5] + ',' + mail[3].replace('\n', ''))[0:50] + '........', + mail[5]) >= 50 else (mail[5] + ',' + mail[3].replace( + '\n', ''))[0:50] + '........', 'msgid': mail[1]}) - for item in data: - meny = TwoLineAvatarIconListItem( - text=item['text'], - secondary_text=item['secondary_text'], - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/text_images/{}.png'.format( - avatarImageFirstLetter(item['secondary_text'].strip())))) - meny.bind(on_press=partial( - self.inbox_detail, item['msgid'])) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial( - self.delete, item['msgid'])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - ach_btn = Button(text='Achieve') - ach_btn.background_color = (0, 1, 0, 1) - ach_btn.bind(on_press=partial( - self.archive, item['msgid'])) - carousel.add_widget(ach_btn) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + self.has_refreshed = True + self.set_mdList(data) + self.children[2].children[0].children[0].bind( + scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -163,8 +140,90 @@ class Inbox(Screen): valign='top') self.ids.ml.add_widget(content) + # pylint: disable=too-many-arguments + def inboxDataQuery(self, xAddress, where, what, start_indx=0, end_indx=20): + """This method used for retrieving inbox data""" + self.queryreturn = kivy_helper_search.search_sql( + xAddress, + self.account, + "inbox", + where, + what, + False, + start_indx, + end_indx) + + def set_mdList(self, data): + """This method is used to create the mdList""" + total_message = len(self.ids.ml.children) + for item in data: + meny = TwoLineAvatarIconListItem( + text=item['text'], + secondary_text=item['secondary_text'], + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget( + AvatarSampleWidget( + source='./images/text_images/{}.png'.format( + avatarImageFirstLetter( + item['secondary_text'].strip())))) + meny.bind( + on_press=partial( + self.inbox_detail, item['msgid'])) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind( + on_press=partial( + self.delete, item['msgid'])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + ach_btn = Button(text='Achieve') + ach_btn.background_color = (0, 1, 0, 1) + ach_btn.bind( + on_press=partial( + self.archive, item['msgid'])) + carousel.add_widget(ach_btn) + carousel.index = 1 + self.ids.ml.add_widget(carousel) + update_message = len(self.ids.ml.children) + self.has_refreshed = True if total_message != update_message else False + + def check_scroll_y(self, instance, somethingelse): + """This method is used to load data on scroll""" + if self.children[2].children[0].children[ + 0].scroll_y <= -0.0 and self.has_refreshed: + self.children[2].children[0].children[0].scroll_y = 0.06 + total_message = len(self.ids.ml.children) + self.update_inbox_screen_on_scroll(total_message) + else: + pass + + def update_inbox_screen_on_scroll(self, total_message, where="", what=""): + """This method is used to load more data on scroll down""" + data = [] + if state.searcing_text: + where = ['subject', 'message'] + what = state.searcing_text + self.inboxDataQuery('toaddress', where, what, total_message, 5) + for mail in self.queryreturn: + # third_text = mail[3].replace('\n', ' ') + data.append({ + 'text': mail[4].strip(), + 'secondary_text': mail[5][:50] + '........' if len( + mail[5]) >= 50 else (mail[5] + ',' + mail[3].replace( + '\n', ''))[0:50] + '........', + 'msgid': mail[1]}) + self.set_mdList(data) + def inbox_detail(self, msg_id, *args): - """Load inbox page details.""" + """Load inbox page details""" state.detailPageType = 'inbox' state.mail_id = msg_id if self.manager: @@ -176,16 +235,17 @@ class Inbox(Screen): src_mng_obj.current = 'mailDetail' def delete(self, data_index, instance, *args): - """Delete inbox mail from inbox listing.""" + """Delete inbox mail from inbox listing""" sqlExecute( "UPDATE inbox SET folder = 'trash' WHERE msgid = ?;", str( data_index)) try: - msg_count_objs = \ - self.parent.parent.parent.parent.children[2].children[0].ids - except Exception as e: - msg_count_objs = \ - self.parent.parent.parent.parent.parent.children[2].children[0].ids + msg_count_objs = ( + self.parent.parent.parent.parent.children[2].children[0].ids) + except Exception: + msg_count_objs = ( + self.parent.parent.parent.parent.parent.children[ + 2].children[0].ids) if int(state.inbox_count) > 0: msg_count_objs.inbox_cnt.badge_text = str( int(state.inbox_count) - 1) @@ -205,7 +265,7 @@ class Inbox(Screen): self.update_trash() def archive(self, data_index, instance, *args): - """Archive inbox mail from inbox listing.""" + """Archive inbox mail from inbox listing""" sqlExecute( "UPDATE inbox SET folder = 'trash' WHERE msgid = ?;", str( data_index)) @@ -213,7 +273,7 @@ class Inbox(Screen): self.update_trash() def update_trash(self): - """Update trash screen mails which is deleted from inbox.""" + """Update trash screen mails which is deleted from inbox""" try: self.parent.screens[4].clear_widgets() self.parent.screens[4].add_widget(Trash()) @@ -223,53 +283,51 @@ class Inbox(Screen): # pylint: disable=attribute-defined-outside-init def refresh_callback(self, *args): - """Method updates the state of application, While the spinner remains on the screen.""" + """Method updates the state of application, + While the spinner remains on the screen""" def refresh_callback(interval): - """Method used for loading the inbox screen data.""" + """Method used for loading the inbox screen data""" state.searcing_text = '' self.children[2].children[1].ids.search_field.text = '' self.ids.ml.clear_widgets() self.loadMessagelist(state.association) + self.has_refreshed = True self.ids.refresh_layout.refresh_done() self.tick = 0 Clock.schedule_once(refresh_callback, 1) + # def set_root_layout(self): + # """Setting root layout""" + # return self.parent.parent.parent + class MyAddress(Screen): - """MyAddress Screen uses screen to show widgets of screens.""" + """MyAddress screen uses screen to show widgets of screens.""" + addresses_list = ListProperty() + has_refreshed = True + is_add_created = False def __init__(self, *args, **kwargs): - """Clock Schdule for method inbox accounts.""" + """Clock schdule for method Myaddress accounts.""" super(MyAddress, self).__init__(*args, **kwargs) Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method inbox accounts.""" + """Clock schdule for method Myaddress accounts""" # pylint: disable=unnecessary-lambda, deprecated-lambda - addresses_list = state.kivyapp.variable_1 + self.addresses_list = state.kivyapp.variable_1 if state.searcing_text: + self.ids.refresh_layout.scroll_y = 1.0 filtered_list = filter( lambda addr: self.filter_address( addr), BMConfigParser().addresses()) - addresses_list = filtered_list - if addresses_list: - data = [] - for address in addresses_list: - data.append({ - 'text': BMConfigParser().get(address, 'label'), - 'secondary_text': address}) - for item in data: - meny = TwoLineAvatarIconListItem( - text=item['text'], - secondary_text=item['secondary_text'], - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/text_images/{}.png'.format(avatarImageFirstLetter(item['text'].strip())))) - meny.bind(on_press=partial( - self.myadd_detail, item['secondary_text'], item['text'])) - self.ids.ml.add_widget(meny) + self.addresses_list = filtered_list + self.addresses_list = [obj for obj in reversed(self.addresses_list)] + if self.addresses_list: + self.has_refreshed = True + self.set_mdList(0, 15) + self.ids.refresh_layout.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -280,90 +338,114 @@ class MyAddress(Screen): size_hint_y=None, valign='top') self.ids.ml.add_widget(content) - if not state.searcing_text: + if not state.searcing_text and not self.is_add_created: try: self.manager.current = 'login' except Exception: pass + def set_mdList(self, first_index, last_index): + """Creating the mdlist""" + data = [] + for address in self.addresses_list[first_index:last_index]: + data.append({ + 'text': BMConfigParser().get(address, 'label'), + 'secondary_text': address}) + for item in data: + meny = TwoLineAvatarIconListItem( + text=item['text'], + secondary_text=item['secondary_text'], + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget(AvatarSampleWidget( + source='./images/text_images/{}.png'.format( + avatarImageFirstLetter(item['text'].strip())))) + meny.bind(on_press=partial( + self.myadd_detail, item['secondary_text'], item['text'])) + self.ids.ml.add_widget(meny) + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll down""" + if self.ids.refresh_layout.scroll_y <= -0.0 and self.has_refreshed: + self.ids.refresh_layout.scroll_y = 0.06 + my_addresses = len(self.ids.ml.children) + if my_addresses != len(self.addresses_list): + self.update_addressBook_on_scroll(my_addresses) + self.has_refreshed = True if my_addresses != len( + self.addresses_list) else False + else: + pass + + def update_addressBook_on_scroll(self, my_addresses): + """Loads more data on scroll down""" + self.set_mdList(my_addresses, my_addresses + 20) + @staticmethod def myadd_detail(fromaddress, label, *args): - """Myaddress Details.""" + """Load myaddresses details""" p = MyaddDetailPopup() p.open() p.set_address(fromaddress, label) # pylint: disable=attribute-defined-outside-init def refresh_callback(self, *args): - """Method updates the state of application, While the spinner remains on the screen.""" + """Method updates the state of application, + While the spinner remains on the screen""" def refresh_callback(interval): - """Method used for loading the myaddress screen data.""" + """Method used for loading the myaddress screen data""" state.searcing_text = '' + state.kivyapp.root.ids.sc10.children[2].active = False self.children[2].children[1].ids.search_field.text = '' + self.has_refreshed = True self.ids.ml.clear_widgets() self.init_ui() self.ids.refresh_layout.refresh_done() self.tick = 0 - Clock.schedule_once(refresh_callback, 1) @staticmethod def filter_address(address): - """Method will filter the my address list data.""" - # pylint: disable=deprecated-lambda + """Method will filter the my address list data""" if filter(lambda x: (state.searcing_text).lower() in x, [ BMConfigParser().get( address, 'label').lower(), address.lower()]): return True return False + def set_root_layout(self): + """Setting root layout""" + return self.manager.parent.parent + class AddressBook(Screen): - """AddressBook Screen uses screen to show widgets of screens.""" + """AddressBook Screen uses screen to show widgets of screens""" + queryreturn = ListProperty() + has_refreshed = True def __init__(self, *args, **kwargs): - """Getting AddressBook Details.""" + """Getting AddressBook Details""" super(AddressBook, self).__init__(*args, **kwargs) Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method AddressBook.""" + """Clock Schdule for method AddressBook""" self.loadAddresslist(None, 'All', '') - print (dt) + print(dt) def loadAddresslist(self, account, where="", what=""): - """Clock Schdule for method AddressBook.""" + """Clock Schdule for method AddressBook""" if state.searcing_text: + self.ids.scroll_y.scroll_y = 1.0 where = ['label', 'address'] what = state.searcing_text xAddress = '' - queryreturn = kivy_helper_search.search_sql( + self.queryreturn = kivy_helper_search.search_sql( xAddress, account, "addressbook", where, what, False) - if queryreturn: - for item in queryreturn: - meny = TwoLineAvatarIconListItem( - text=item[0], - secondary_text=item[1], - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/text_images/{}.png'.format(avatarImageFirstLetter(item[0].strip())))) - meny.bind(on_press=partial( - self.addBook_detail, item[1], item[0])) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial(self.delete_address, item[1])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + self.queryreturn = [obj for obj in reversed(self.queryreturn)] + if self.queryreturn: + self.has_refreshed = True + self.set_mdList(0, 20) + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -375,22 +457,66 @@ class AddressBook(Screen): valign='top') self.ids.ml.add_widget(content) + def set_mdList(self, start_index, end_index): + """Creating the mdList""" + for item in self.queryreturn[start_index:end_index]: + meny = TwoLineAvatarIconListItem( + text=item[0], + secondary_text=item[1], + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget(AvatarSampleWidget( + source='./images/text_images/{}.png'.format( + avatarImageFirstLetter(item[0].strip())))) + meny.bind(on_press=partial( + self.addBook_detail, item[1], item[0])) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind(on_press=partial(self.delete_address, item[1])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + carousel.index = 1 + self.ids.ml.add_widget(carousel) + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + exist_addresses = len(self.ids.ml.children) + if exist_addresses != len(self.queryreturn): + self.update_addressBook_on_scroll(exist_addresses) + self.has_refreshed = True if exist_addresses != len( + self.queryreturn) else False + else: + pass + + def update_addressBook_on_scroll(self, exist_addresses): + """Load more data on scroll down""" + self.set_mdList(exist_addresses, exist_addresses + 5) + @staticmethod def refreshs(*args): - """Refresh the Widget.""" + """Refresh the Widget""" # state.navinstance.ids.sc11.ids.ml.clear_widgets() # state.navinstance.ids.sc11.loadAddresslist(None, 'All', '') pass @staticmethod def addBook_detail(address, label, *args): - """Addressbook Details.""" + """Addressbook details""" p = AddbookDetailPopup() p.open() p.set_addbook_data(address, label) def delete_address(self, address, instance, *args): - """Delete inbox mail from inbox listing.""" + """Delete inbox mail from inbox listing""" self.ids.ml.remove_widget(instance.parent.parent) sqlExecute( "DELETE FROM addressbook WHERE address = '{}';".format(address)) @@ -442,33 +568,35 @@ class RV(RecycleView): class DropDownWidget(BoxLayout): - """Adding Dropdown Widget.""" - + """Adding Dropdown Widget""" + # pylint: disable=too-many-statements, too-many-locals + # pylint: disable=inconsistent-return-statements txt_input = ObjectProperty() rv = ObjectProperty() - def send(self, navApp): # pylint: disable=too-many-statements, inconsistent-return-statements - """Send message from one address to another.""" - # pylint: disable=too-many-locals + def send(self, navApp): + """Send message from one address to another""" fromAddress = str(self.ids.ti.text) toAddress = str(self.ids.txt_input.text) subject = self.ids.subject.text.strip() - message = self.ids.body.text.strip() + message = self.ids.subject.text.strip() encoding = 3 print ("message: ", self.ids.body.text) sendMessageToPeople = True if sendMessageToPeople: if toAddress != '' and subject and message: from addresses import decodeAddress - status, addressVersionNumber, streamNumber, ripe = \ - decodeAddress(toAddress) + status, addressVersionNumber, streamNumber, ripe = ( + decodeAddress(toAddress)) if status == 'success': - if state.detailPageType == 'draft' and state.send_draft_mail: + navApp.root.ids.sc3.children[0].active = True + if state.detailPageType == 'draft' \ + and state.send_draft_mail: sqlExecute( - "UPDATE sent SET toaddress = ? \ - , fromaddress = ? , subject = ?\ - , message = ?, folder = 'sent'\ - WHERE ackdata = ?;", + "UPDATE sent SET toaddress = ?" + ", fromaddress = ? , subject = ?" + ", message = ?, folder = 'sent'" + " WHERE ackdata = ?;", toAddress, fromAddress, subject, @@ -482,18 +610,19 @@ class DropDownWidget(BoxLayout): from addresses import addBMIfNotPresent toAddress = addBMIfNotPresent(toAddress) statusIconColor = 'red' - if addressVersionNumber > 4 or addressVersionNumber <= 1: - print("addressVersionNumber > 4 \ - or addressVersionNumber <= 1") + if (addressVersionNumber > 4) or ( + addressVersionNumber <= 1): + print ("addressVersionNumber > 4"\ + " or addressVersionNumber <= 1") if streamNumber > 1 or streamNumber == 0: - print("streamNumber > 1 or streamNumber == 0") + print ("streamNumber > 1 or streamNumber == 0") if statusIconColor == 'red': - print("shared.statusIconColor == 'red'") + print ("shared.statusIconColor == 'red'") stealthLevel = BMConfigParser().safeGetInt( 'bitmessagesettings', 'ackstealthlevel') from helper_ackPayload import genAckPayload ackdata = genAckPayload(streamNumber, stealthLevel) - t = () + # t = () sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', @@ -515,20 +644,17 @@ class DropDownWidget(BoxLayout): 'bitmessagesettings', 'ttl'))) state.check_sent_acc = fromAddress state.msg_counter_objs = self.parent.parent.parent.parent\ - .parent.parent.children[0].children[2].children[0].ids - self.parent.parent.screens[3].ids.ml.clear_widgets() - self.parent.parent.screens[3].loadSent(state.association) - self.parent.parent.screens[16].clear_widgets() - self.parent.parent.screens[16].add_widget(Allmails()) - toLabel = '' - + .parent.parent.children[2].children[0].ids + # self.parent.parent.screens[0].ids.ml.clear_widgets() + # self.parent.parent.screens[0].loadMessagelist(state.association) + self.parent.parent.screens[3].update_sent_messagelist() + # self.parent.parent.screens[16].clear_widgets() + # self.parent.parent.screens[16].add_widget(Allmails()) + Clock.schedule_once(self.callback_for_msgsend, 3) queues.workerQueue.put(('sendmessage', toAddress)) print ("sqlExecute successfully #######################") - self.parent.parent.current = 'inbox' state.in_composer = True - navApp.back_press() - toast('send') - return None + return else: msg = 'Enter a valid recipients address' elif not toAddress: @@ -537,9 +663,17 @@ class DropDownWidget(BoxLayout): msg = 'Please fill the form' self.address_error_message(msg) + @staticmethod + def callback_for_msgsend(dt=0): + """Callback method for messagesend""" + state.kivyapp.root.ids.sc3.children[0].active = False + state.in_sent_method = True + state.kivyapp.back_press() + toast('sent') + # pylint: disable=attribute-defined-outside-init def address_error_message(self, msg): - """Show Error Message.""" + """Generates error message""" msg_dialog = MDDialog( text=msg, title='', size_hint=(.8, .25), text_button_ok='Ok', @@ -548,11 +682,11 @@ class DropDownWidget(BoxLayout): @staticmethod def callback_for_menu_items(text_item): - """Method is used for getting the callback of alert box""" + """Callback of alert box""" toast(text_item) def reset_composer(self): - """Method will reset composer.""" + """Method will reset composer""" self.ids.ti.text = '' self.ids.btn.text = 'Select' self.ids.txt_input.text = '' @@ -561,7 +695,7 @@ class DropDownWidget(BoxLayout): toast("Reset message") def auto_fill_fromaddr(self): - """Mehtod used to fill the text automatically From Address.""" + """Fill the text automatically From Address""" self.ids.ti.text = self.ids.btn.text self.ids.ti.focus = True @@ -604,18 +738,23 @@ class MyTextInput(TextInput): class Payment(Screen): - """Payment Method.""" + """Payment module""" - def get_available_credits(self, instance): # pylint: disable=no-self-use - """Method helps to get the available credits""" + def get_available_credits(self, instance): # pylint: disable=no-self-use + """Get the available credits""" state.availabe_credit = instance.parent.children[1].text - existing_credits = state.kivyapp.root.ids.sc18.ids.ml.children[0].children[0].children[0].children[0].text + existing_credits = ( + state.kivyapp.root.ids.sc18.ids.ml.children[0].children[ + 0].children[0].children[0].text) if len(existing_credits.split(' ')) > 1: - toast('We already have added free coins for the subscription to your account!') + toast( + 'We already have added free coins' + ' for the subscription to your account!') else: toast('Coins added to your account!') - state.kivyapp.root.ids.sc18.ids.ml.children[0].children[0].children[ - 0].children[0].text = '{0}'.format(state.availabe_credit) + state.kivyapp.root.ids.sc18.ids.ml.children[0].children[ + 0].children[0].children[0].text = '{0}'.format( + state.availabe_credit) class Credits(Screen): @@ -631,8 +770,7 @@ class Login(Screen): class NetworkStat(Screen): - """Method used to show network stat.""" - + """Method used to show network stat""" text_variable_1 = StringProperty( '{0}::{1}'.format('Total Connections', '0')) text_variable_2 = StringProperty( @@ -644,12 +782,12 @@ class NetworkStat(Screen): 'Processed {0} object to be synced'.format('0')) def __init__(self, *args, **kwargs): - """Init method for network stat.""" + """Init method for network stat""" super(NetworkStat, self).__init__(*args, **kwargs) Clock.schedule_interval(self.init_ui, 1) def init_ui(self, dt=0): - """Clock Schdule for method inbox accounts.""" + """Clock Schdule for method networkstat screen""" import network.stats import shared from network import objectracker @@ -672,19 +810,22 @@ class ContentNavigationDrawer(Navigatorss): class Random(Screen): - """Generates Random Address.""" - + """Generates Random Address""" is_active = BooleanProperty(False) checked = StringProperty("") def generateaddress(self, navApp): - """Method for Address Generator.""" + """Method for Address Generator""" + entered_label = str(self.ids.label.text).strip() streamNumberForAddress = 1 label = self.ids.label.text eighteenByteRipe = False nonceTrialsPerByte = 1000 payloadLengthExtraBytes = 1000 - if str(self.ids.label.text).strip(): + lables = [BMConfigParser().get(obj, 'label') + for obj in BMConfigParser().addresses()] + if entered_label and entered_label not in lables: + toast('Address Creating...') queues.addressGeneratorQueue.put(( 'createRandomAddress', 4, streamNumberForAddress, @@ -692,13 +833,40 @@ class Random(Screen): nonceTrialsPerByte, payloadLengthExtraBytes)) self.ids.label.text = '' - self.parent.parent.parent.parent.ids.toolbar.opacity = 1 - self.parent.parent.parent.parent.ids.toolbar.disabled = False - self.parent.parent.parent.parent.ids.sc10.ids.ml.clear_widgets() + self.parent.parent.children[1].opacity = 1 + self.parent.parent.children[1].disabled = False + state.kivyapp.root.ids.sc10.children[1].active = True self.manager.current = 'myaddress' - self.parent.parent.parent.parent.ids.sc10.init_ui() - self.manager.current = 'myaddress' - toast('New address created') + Clock.schedule_once(self.address_created_callback, 6) + + @staticmethod + def address_created_callback(dt=0): + """New address created""" + state.kivyapp.root.ids.sc10.children[1].active = False + state.kivyapp.root.ids.sc10.ids.ml.clear_widgets() + state.kivyapp.root.ids.sc10.is_add_created = True + state.kivyapp.root.ids.sc10.init_ui() + toast('New address created') + + def add_validation(self, instance): + """Checking validation at address creation time""" + entered_label = str(instance.text.strip()) + lables = [BMConfigParser().get(obj, 'label') + for obj in BMConfigParser().addresses()] + if entered_label in lables: + self.ids.label.error = True + self.ids.label.helper_text = 'Label name is already exist you'\ + ' can try this Ex. ( {0}_1, {0}_2 )'.format( + entered_label) + elif entered_label: + self.ids.label.error = False + else: + self.ids.label.error = False + self.ids.label.helper_text = 'This field is required' + + def reset_address_label(self): + """Resetting address labels""" + self.ids.label.text = '' class AddressSuccessful(Screen): @@ -708,7 +876,11 @@ class AddressSuccessful(Screen): class Sent(Screen): - """Sent Screen uses screen to show widgets of screens.""" + """Sent Screen uses screen to show widgets of screens""" + queryreturn = ListProperty() + has_refreshed = True + account = StringProperty() + def __init__(self, *args, **kwargs): """Association with the screen.""" super(Sent, self).__init__(*args, **kwargs) @@ -718,74 +890,32 @@ class Sent(Screen): Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method sent accounts.""" - self.sentaccounts() + """Clock Schdule for method sent accounts""" + self.loadSent() print(dt) - def sentaccounts(self): - """Load sent accounts.""" - account = state.association - self.loadSent(account, 'All', '') - - def loadSent(self, account, where="", what=""): + def loadSent(self, where="", what=""): """Load Sent list for Sent messages.""" + self.account = state.association if state.searcing_text: + self.ids.scroll_y.scroll_y = 1.0 where = ['subject', 'message'] what = state.searcing_text xAddress = 'fromaddress' data = [] - queryreturn = kivy_helper_search.search_sql( - xAddress, account, "sent", where, what, False) - if state.msg_counter_objs and state.association == \ - state.check_sent_acc: - state.msg_counter_objs.send_cnt.badge_text = str(len(queryreturn)) - state.sent_count = str(int(state.sent_count) + 1) - state.all_count = str(int(state.all_count) + 1) - state.msg_counter_objs.allmail_cnt.badge_text = state.all_count - state.check_sent_acc = None - - if queryreturn: - src_mng_obj = state.kivyapp.root.children[2].children[0].ids - src_mng_obj.send_cnt.badge_text = str(len(queryreturn)) - state.sent_count = str(len(queryreturn)) - for mail in queryreturn: + self.sentDataQuery(xAddress, where, what) + if self.queryreturn: + self.set_sentCount(state.sent_count) + for mail in self.queryreturn: data.append({ 'text': mail[1].strip(), 'secondary_text': mail[2][:50] + '........' if len( - mail[2]) >= 50 else ( - mail[2] + ',' + mail[3].replace('\n', ''))[0:50] + '........', + mail[2]) >= 50 else (mail[2] + ',' + mail[3].replace( + '\n', ''))[0:50] + '........', 'ackdata': mail[5]}) - for item in data: - meny = TwoLineAvatarIconListItem( - text=item['text'], - secondary_text=item['secondary_text'], - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/text_images/{}.png'.format( - avatarImageFirstLetter(item['secondary_text'].strip())))) - meny.bind(on_press=partial( - self.sent_detail, item['ackdata'])) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial( - self.delete, item['ackdata'])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - ach_btn = Button(text='Achieve') - ach_btn.background_color = (0, 1, 0, 1) - ach_btn.bind(on_press=partial( - self.archive, item['ackdata'])) - carousel.add_widget(ach_btn) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + self.set_mdlist(data, 0) + self.has_refreshed = True + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -797,8 +927,117 @@ class Sent(Screen): valign='top') self.ids.ml.add_widget(content) + # pylint: disable=too-many-arguments + def sentDataQuery(self, xAddress, where, what, start_indx=0, end_indx=20): + """This method is used to retrieving data from sent table""" + self.queryreturn = kivy_helper_search.search_sql( + xAddress, + self.account, + "sent", + where, + what, + False, + start_indx, + end_indx) + + def set_mdlist(self, data, set_index=0): + """This method is used to create the mdList""" + total_sent_msg = len(self.ids.ml.children) + for item in data: + meny = TwoLineAvatarIconListItem( + text=item['text'], + secondary_text=item['secondary_text'], + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget(AvatarSampleWidget( + source='./images/text_images/{}.png'.format( + avatarImageFirstLetter(item['secondary_text'].strip())))) + meny.bind(on_press=partial( + self.sent_detail, item['ackdata'])) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind(on_press=partial( + self.delete, item['ackdata'])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + ach_btn = Button(text='Achieve') + ach_btn.background_color = (0, 1, 0, 1) + ach_btn.bind(on_press=partial( + self.archive, item['ackdata'])) + carousel.add_widget(ach_btn) + carousel.index = 1 + self.ids.ml.add_widget(carousel, index=set_index) + updated_msgs = len(self.ids.ml.children) + self.has_refreshed = True if total_sent_msg != updated_msgs else False + + def update_sent_messagelist(self): + """This method is used to update screen when new mail is sent""" + self.account = state.association + if len(self.ids.ml.children) < 3: + self.ids.ml.clear_widgets() + self.loadSent() + total_sent = int(state.sent_count) + 1 + self.set_sentCount(total_sent) + else: + data = [] + self.sentDataQuery('fromaddress', '', '', 0, 1) + total_sent = int(state.sent_count) + 1 + self.set_sentCount(total_sent) + for mail in self.queryreturn: + data.append({ + 'text': mail[1].strip(), + 'secondary_text': mail[2][:50] + '........' if len( + mail[2]) >= 50 else (mail[2] + ',' + mail[3].replace( + '\n', ''))[0:50] + '........', + 'ackdata': mail[5]}) + self.set_mdlist(data, total_sent - 1) + if state.msg_counter_objs and state.association == ( + state.check_sent_acc): + state.all_count = str(int(state.all_count) + 1) + state.msg_counter_objs.allmail_cnt.badge_text = state.all_count + state.check_sent_acc = None + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll down""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_sent_msg = len(self.ids.ml.children) + self.update_sent_screen_on_scroll(total_sent_msg) + else: + pass + + def update_sent_screen_on_scroll(self, total_sent_msg, where="", what=""): + """This method is used to load more data on scroll down""" + if state.searcing_text: + where = ['subject', 'message'] + what = state.searcing_text + self.sentDataQuery('fromaddress', where, what, total_sent_msg, 5) + data = [] + for mail in self.queryreturn: + data.append({ + 'text': mail[1].strip(), + 'secondary_text': mail[2][:50] + '........' if len( + mail[2]) >= 50 else (mail[2] + ',' + mail[3].replace( + '\n', ''))[0:50] + '........', + 'ackdata': mail[5]}) + self.set_mdlist(data, 0) + + @staticmethod + def set_sentCount(total_sent): + """Set the total no. of sent message count""" + src_mng_obj = state.kivyapp.root.children[2].children[0].ids + src_mng_obj.send_cnt.badge_text = str(total_sent) + state.sent_count = str(total_sent) + def sent_detail(self, ackdata, *args): - """Load sent mail details.""" + """Load sent mail details""" state.detailPageType = 'sent' state.mail_id = ackdata if self.manager: @@ -810,7 +1049,7 @@ class Sent(Screen): src_mng_obj.current = 'mailDetail' def delete(self, data_index, instance, *args): - """Delete sent mail from sent mail listing.""" + """Delete sent mail from sent mail listing""" try: msg_count_objs = self.parent.parent.parent.parent.children[ 2].children[0].ids @@ -828,22 +1067,22 @@ class Sent(Screen): state.trash_count = str(int(state.trash_count) + 1) state.all_count = str(int(state.all_count) - 1) sqlExecute( - "UPDATE sent SET folder = 'trash' \ - WHERE ackdata = ?;", str(data_index)) + "UPDATE sent SET folder = 'trash'" + " WHERE ackdata = ?;", str(data_index)) self.ids.ml.remove_widget(instance.parent.parent) toast('Deleted') self.update_trash() def archive(self, data_index, instance, *args): - """Archive sent mail from sent mail listing.""" + """Archive sent mail from sent mail listing""" sqlExecute( - "UPDATE sent SET folder = 'trash' \ - WHERE ackdata = ?;", str(data_index)) + "UPDATE sent SET folder = 'trash'" + " WHERE ackdata = ?;", str(data_index)) self.ids.ml.remove_widget(instance.parent.parent) self.update_trash() def update_trash(self): - """Update trash screen mails which is deleted from inbox.""" + """Update trash screen mails which is deleted from inbox""" try: self.parent.screens[4].clear_widgets() self.parent.screens[4].add_widget(Trash()) @@ -857,60 +1096,28 @@ class Sent(Screen): class Trash(Screen): - """Trash Screen uses screen to show widgets of screens.""" + """Trash Screen uses screen to show widgets of screens""" + trash_messages = ListProperty() + has_refreshed = True + delete_index = StringProperty() + table_name = StringProperty() def __init__(self, *args, **kwargs): - """Trash method, delete sent message and add in Trash.""" + """Trash method, delete sent message and add in Trash""" super(Trash, self).__init__(*args, **kwargs) Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method inbox accounts.""" + """Clock Schdule for method trash screen.""" if state.association == '': if BMConfigParser().addresses(): state.association = BMConfigParser().addresses()[0] - - inbox = sqlQuery( - "SELECT toaddress, fromaddress, subject, message, folder, received from \ - inbox WHERE folder = 'trash' and toaddress = '{}';".format( - state.association)) - sent = sqlQuery( - "SELECT toaddress, fromaddress, subject, message, folder, lastactiontime from \ - sent WHERE folder = 'trash' and fromaddress = '{}';".format( - state.association)) - trash_data = inbox + sent - - if trash_data: + self.trashDataQuery(0, 20) + if self.trash_messages: src_mng_obj = state.kivyapp.root.children[2].children[0].ids - src_mng_obj.trash_cnt.badge_text = str(len(trash_data)) - state.trash_count = str(len(trash_data)) - for item in trash_data: - meny = TwoLineAvatarIconListItem( - text=item[1], - secondary_text=item[2][:50] + '........' if len( - item[2]) >= 50 else ( - item[2] + ',' + item[3].replace('\n', ''))[0:50] + '........', - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - img_latter = './images/text_images/{}.png'.format( - item[2][0].upper() if (item[2][0].upper() >= 'A' and item[ - 2][0].upper() <= 'Z') else '!') - meny.add_widget(AvatarSampleWidget(source=img_latter)) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial( - self.delete_permanently, item[5])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + src_mng_obj.trash_cnt.badge_text = state.trash_count + self.set_mdList() + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -921,9 +1128,114 @@ class Trash(Screen): valign='top') self.ids.ml.add_widget(content) - def delete_permanently(self, data_index, instance, *args): - """Deleting trash mail permanently.""" - pass + def trashDataQuery(self, start_indx, end_indx): + """Trash message query""" + self.trash_messages = sqlQuery( + "SELECT toaddress, fromaddress, subject, message," + " folder ||',' || 'sent' as folder, ackdata As" + " id, DATE(lastactiontime) As actionTime FROM sent" + " WHERE folder = 'trash' and fromaddress = '{0}' UNION" + " SELECT toaddress, fromaddress, subject, message," + " folder ||',' || 'inbox' as folder, msgid As id," + " DATE(received) As actionTime FROM inbox" + " WHERE folder = 'trash' and toaddress = '{0}'" + " ORDER BY actionTime DESC limit {1}, {2}".format( + state.association, start_indx, end_indx)) + + def set_mdList(self): + """This method is used to create the mdlist""" + total_trash_msg = len(self.ids.ml.children) + for item in self.trash_messages: + meny = TwoLineAvatarIconListItem( + text=item[1], + secondary_text=item[2][:50] + '........' if len( + item[2]) >= 50 else (item[2] + ',' + item[3].replace( + '\n', ''))[0:50] + '........', + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + img_latter = './images/text_images/{}.png'.format( + item[2][0].upper() if (item[2][0].upper() >= 'A' and item[ + 2][0].upper() <= 'Z') else '!') + meny.add_widget(AvatarSampleWidget(source=img_latter)) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind(on_press=partial( + self.delete_permanently, item[5], item[4])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + carousel.index = 1 + self.ids.ml.add_widget(carousel) + self.has_refreshed = True if total_trash_msg != len( + self.ids.ml.children) else False + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_trash_msg = len(self.ids.ml.children) + self.update_trash_screen_on_scroll(total_trash_msg) + else: + pass + + def update_trash_screen_on_scroll(self, total_trash_msg): + """Load more data on scroll down""" + self.trashDataQuery(total_trash_msg, 5) + self.set_mdList() + + def delete_permanently(self, data_index, folder, instance, *args): + """Deleting trash mail permanently""" + self.table_name = folder.split(',')[1] + self.delete_index = data_index + self.delete_confirmation() + + def callback_for_screen_load(self, dt=0): + """This methos is for loading screen""" + self.ids.ml.clear_widgets() + self.init_ui(0) + self.children[1].active = False + toast('Message is permanently deleted') + + def delete_confirmation(self): + """Show confirmation delete popup""" + delete_msg_dialog = MDDialog( + text='Are you sure you want to delete this' + ' message permanently from trash?', + title='', + size_hint=(.8, .25), + text_button_ok='Yes', + text_button_cancel='No', + events_callback=self.callback_for_delete_msg) + delete_msg_dialog.open() + + def callback_for_delete_msg(self, text_item): + """Getting the callback of alert box""" + if text_item == 'Yes': + self.delete_message_from_trash() + else: + toast(text_item) + + def delete_message_from_trash(self): + """Deleting message from trash""" + self.children[1].active = True + if self.table_name == 'inbox': + sqlExecute("DELETE FROM inbox WHERE msgid = ?;", str( + self.delete_index)) + elif self.table_name == 'sent': + sqlExecute("DELETE FROM sent WHERE ackdata = ?;", str( + self.delete_index)) + msg_count_objs = state.kivyapp.root.children[2].children[0].ids + if int(state.trash_count) > 0: + msg_count_objs.trash_cnt.badge_text = str( + int(state.trash_count) - 1) + state.trash_count = str(int(state.trash_count) - 1) + Clock.schedule_once(self.callback_for_screen_load, 1) class Page(Screen): @@ -952,9 +1264,8 @@ class Setting(Screen): pass -class NavigateApp(App): # pylint: disable=too-many-public-methods - """Navigation Layout of class.""" - +class NavigateApp(App): # pylint: disable=too-many-public-methods + """Navigation Layout of class""" theme_cls = ThemeManager() previous_date = ObjectProperty() obj_1 = ObjectProperty() @@ -983,7 +1294,7 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods ] def build(self): - """Method builds the widget.""" + """Method builds the widget""" main_widget = Builder.load_file( os.path.join(os.path.dirname(__file__), 'main.kv')) self.nav_drawer = Navigatorss() @@ -995,14 +1306,14 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods return main_widget def run(self): - """Running the widgets.""" + """Running the widgets""" kivyuisignaler.release() super(NavigateApp, self).run() # pylint: disable=inconsistent-return-statements @staticmethod def showmeaddresses(name="text"): - """Show the addresses in spinner to make as dropdown.""" + """Show the addresses in spinner to make as dropdown""" if name == "text": if BMConfigParser().addresses(): return BMConfigParser().addresses()[0][:16] + '..' @@ -1014,111 +1325,109 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods return "valuesdemo" def getCurrentAccountData(self, text): - """Get Current Address Account Data.""" + """Get Current Address Account Data""" self.set_identicon(text) address_label = self.current_address_label( BMConfigParser().get(text, 'label'), text) self.root_window.children[1].ids.toolbar.title = address_label state.association = text state.searcing_text = '' + LoadingPopup().open() + self.set_message_count() + Clock.schedule_once(self.setCurrentAccountData, 0.5) + + def setCurrentAccountData(self, dt=0): + """This method set the current accout data on all the screens.""" self.root.ids.sc1.ids.ml.clear_widgets() self.root.ids.sc1.loadMessagelist(state.association) + + self.root.ids.sc4.ids.ml.clear_widgets() + self.root.ids.sc4.children[2].children[1].ids.search_field.text = '' + self.root.ids.sc4.loadSent(state.association) + + self.root.ids.sc16.clear_widgets() + self.root.ids.sc16.add_widget(Draft()) + + self.root.ids.sc5.clear_widgets() + self.root.ids.sc5.add_widget(Trash()) + + self.root.ids.sc17.clear_widgets() + self.root.ids.sc17.add_widget(Allmails()) + self.root.ids.scr_mngr.current = 'inbox' - msg_counter_objs = \ - self.root_window.children[1].children[2].children[0].ids - state.sent_count = str( - sqlQuery( - "SELECT COUNT(*) FROM sent WHERE fromaddress = '{}' and \ - folder = 'sent' ;".format(state.association))[0][0]) - state.inbox_count = str( - sqlQuery( - "SELECT COUNT(*) FROM inbox WHERE toaddress = '{}' and \ - folder = 'inbox' ;".format(state.association))[0][0]) - state.trash_count = str(sqlQuery("SELECT (SELECT count(*) FROM sent \ - where fromaddress = '{0}' and folder = 'trash' ) \ - +(SELECT count(*) FROM inbox where toaddress = '{0}' and \ - folder = 'trash') AS SumCount".format(state.association))[0][0]) - state.draft_count = str( - sqlQuery( - "SELECT COUNT(*) FROM sent WHERE fromaddress = '{}' and \ - folder = 'draft' ;".format(state.association))[0][0]) - state.all_count = str(int(state.sent_count) + int(state.inbox_count)) - - if msg_counter_objs: - msg_counter_objs.send_cnt.badge_text = state.sent_count - msg_counter_objs.inbox_cnt.badge_text = state.inbox_count - msg_counter_objs.trash_cnt.badge_text = state.trash_count - msg_counter_objs.draft_cnt.badge_text = state.draft_count - msg_counter_objs.allmail_cnt.badge_text = state.all_count - @staticmethod def getCurrentAccount(): - """It uses to get current account label.""" + """It uses to get current account label""" if state.association: return state.association return "Bitmessage Login" @staticmethod def addingtoaddressbook(): - """Adding to address Book.""" + """Adding to address Book""" p = GrashofPopup() p.open() def getDefaultAccData(self): - """Getting Default Account Data.""" + """Getting Default Account Data""" if BMConfigParser().addresses(): img = identiconGeneration.generate(BMConfigParser().addresses()[0]) self.createFolder('./images/default_identicon/') if platform == 'android': - # android_path = os.path.expanduser("~/user/0/org.test.bitapp/files/app/") - android_path = os.path.join(os.environ['ANDROID_PRIVATE'] + '/app/') + # android_path = os.path.expanduser + # ("~/user/0/org.test.bitapp/files/app/") + android_path = os.path.join( + os.environ['ANDROID_PRIVATE'] + '/app/') img.texture.save('{1}/images/default_identicon/{0}.png'.format( BMConfigParser().addresses()[0], android_path)) else: - img.texture.save('./images/default_identicon/{}.png'.format(BMConfigParser().addresses()[0])) + img.texture.save( + './images/default_identicon/{}.png'.format( + BMConfigParser().addresses()[0])) return BMConfigParser().addresses()[0] return 'Select Address' @staticmethod def createFolder(directory): - """This method is used to create the directory when app starts""" + """Create directory when app starts""" try: if not os.path.exists(directory): os.makedirs(directory) except OSError: - print('Error: Creating directory. ' + directory) + print ('Error: Creating directory. ' + directory) @staticmethod def get_default_image(): """Getting default image on address""" if BMConfigParser().addresses(): - return './images/default_identicon/{}.png'.format(BMConfigParser().addresses()[0]) + return './images/default_identicon/{}.png'.format( + BMConfigParser().addresses()[0]) return './images/no_identicons.png' @staticmethod def addressexist(): - """Checking address existence.""" + """Checking address existence""" if BMConfigParser().addresses(): return True return False def on_key(self, window, key, *args): - """Method is used for going on previous screen.""" + """Method is used for going on previous screen""" if key == 27: - if self.root.ids.scr_mngr.current == "mailDetail": + if state.in_search_mode and self.root.ids.scr_mngr.current != ( + "mailDetail"): + self.closeSearchScreen() + elif self.root.ids.scr_mngr.current == "mailDetail": self.root.ids.scr_mngr.current = 'sent'\ if state.detailPageType == 'sent' else 'inbox' \ if state.detailPageType == 'inbox' else 'draft' self.back_press() elif self.root.ids.scr_mngr.current == "create": - composer_objs = self.root - from_addr = str(self.root.ids.sc3.children[0].ids.ti.text) - to_addr = str(self.root.ids.sc3.children[0].ids.txt_input.text) - if from_addr and to_addr and state.detailPageType != 'draft': - Draft().draft_msg(composer_objs) + self.save_draft() + self.set_common_header() + state.in_composer = False self.root.ids.scr_mngr.current = 'inbox' - self.back_press() elif self.root.ids.scr_mngr.current == "showqrcode": self.root.ids.scr_mngr.current = 'myaddress' elif self.root.ids.scr_mngr.current == "random": @@ -1128,45 +1437,104 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods self.root.ids.scr_mngr.transition.direction = 'right' self.root.ids.scr_mngr.transition.bind(on_complete=self.reset) return True + elif key == 13: + if state.search_screen == 'inbox' and state.searcing_text: + self.root.ids.sc1.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'addressbook': + self.root.ids.sc11.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'myaddress': + self.root.ids.sc10.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'sent': + self.root.ids.sc4.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + + def search_callback(self, dt=0): + """Show data after loader is loaded""" + if state.search_screen == 'inbox': + self.root.ids.sc1.ids.ml.clear_widgets() + self.root.ids.sc1.loadMessagelist(state.association) + self.root.ids.sc1.children[1].active = False + elif state.search_screen == 'addressbook': + self.root.ids.sc11.ids.ml.clear_widgets() + self.root.ids.sc11.loadAddresslist(None, 'All', '') + self.root.ids.sc11.children[1].active = False + elif state.search_screen == 'myaddress': + self.root.ids.sc10.ids.ml.clear_widgets() + self.root.ids.sc10.init_ui() + self.root.ids.sc10.children[1].active = False + else: + self.root.ids.sc4.ids.ml.clear_widgets() + self.root.ids.sc4.loadSent(state.association) + self.root.ids.sc4.children[1].active = False + self.root.ids.scr_mngr.current = state.search_screen + + def save_draft(self): + """Saving drafts messages""" + composer_objs = self.root + from_addr = str(self.root.ids.sc3.children[1].ids.ti.text) + to_addr = str(self.root.ids.sc3.children[1].ids.txt_input.text) + if from_addr and to_addr and state.detailPageType != 'draft' \ + and not state.in_sent_method: + Draft().draft_msg(composer_objs) + return def reset(self, *args): - """Method used to set transition direction.""" + """Set transition direction""" self.root.ids.scr_mngr.transition.direction = 'left' self.root.ids.scr_mngr.transition.unbind(on_complete=self.reset) @staticmethod def status_dispatching(data): - """Method used for status dispatching acknowledgment.""" + """Dispatching Status acknowledgment""" ackData, message = data if state.ackdata == ackData: state.status.status = message def clear_composer(self): - """If slow down the nwe will make new composer edit screen.""" + """If slow down, the new composer edit screen""" self.set_navbar_for_composer() - # self.root.ids.search_bar.clear_widgets() - composer_obj = self.root.ids.sc3.children[0].ids + composer_obj = self.root.ids.sc3.children[1].ids composer_obj.ti.text = '' composer_obj.btn.text = 'Select' composer_obj.txt_input.text = '' composer_obj.subject.text = '' composer_obj.body.text = '' state.in_composer = True + state.in_sent_method = False def set_navbar_for_composer(self): - """This method is used for clearing toolbar data when composer open""" + """Clearing toolbar data when composer open""" self.root.ids.toolbar.left_action_items = [ ['arrow-left', lambda x: self.back_press()]] self.root.ids.toolbar.right_action_items = [ - ['refresh', lambda x: self.root.ids.sc3.children[0].reset_composer()], - ['send', lambda x: self.root.ids.sc3.children[0].send(self)]] + ['refresh', + lambda x: self.root.ids.sc3.children[1].reset_composer()], + ['send', + lambda x: self.root.ids.sc3.children[1].send(self)]] + + def set_common_header(self): + """Common header for all window""" + self.root.ids.toolbar.right_action_items = [ + ['account-plus', lambda x: self.addingtoaddressbook()]] + self.root.ids.toolbar.left_action_items = [ + ['menu', lambda x: self.root.toggle_nav_drawer()]] + return def back_press(self): - """Method used for going back from composer to previous page.""" - self.root.ids.toolbar.right_action_items = \ - [['account-plus', lambda x: self.addingtoaddressbook()]] - self.root.ids.toolbar.left_action_items = \ - [['menu', lambda x: self.root.toggle_nav_drawer()]] + """Method for, reverting composer to previous page""" + self.save_draft() + if self.root.ids.scr_mngr.current == \ + 'mailDetail' and state.in_search_mode: + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.closeSearchScreen()]] + toolbar_obj.right_action_items = [] + self.root.ids.toolbar.title = '' + else: + self.set_common_header() self.root.ids.scr_mngr.current = 'inbox' \ if state.in_composer else 'allmails'\ if state.is_allmail else state.detailPageType\ @@ -1178,16 +1546,63 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods state.detailPageType = '' state.in_composer = False + @staticmethod + def get_inbox_count(): + """Getting inbox count""" + state.inbox_count = str( + sqlQuery( + "SELECT COUNT(*) FROM inbox WHERE toaddress = '{}' and" + " folder = 'inbox' ;".format(state.association))[0][0]) + + @staticmethod + def get_sent_count(): + """Getting sent count""" + state.sent_count = str( + sqlQuery( + "SELECT COUNT(*) FROM sent WHERE fromaddress = '{}' and" + " folder = 'sent' ;".format(state.association))[0][0]) + + def set_message_count(self): + """Setting message count""" + try: + msg_counter_objs = ( + self.root_window.children[0].children[2].children[0].ids) + except Exception: + msg_counter_objs = ( + self.root_window.children[2].children[2].children[0].ids) + self.get_inbox_count() + self.get_sent_count() + state.trash_count = str(sqlQuery( + "SELECT (SELECT count(*) FROM sent" + " where fromaddress = '{0}' and folder = 'trash' )" + "+(SELECT count(*) FROM inbox where toaddress = '{0}' and" + " folder = 'trash') AS SumCount".format(state.association))[0][0]) + state.draft_count = str( + sqlQuery( + "SELECT COUNT(*) FROM sent WHERE fromaddress = '{}' and" + " folder = 'draft' ;".format(state.association))[0][0]) + state.all_count = str(int(state.sent_count) + int(state.inbox_count)) + + if msg_counter_objs: + msg_counter_objs.send_cnt.badge_text = state.sent_count + msg_counter_objs.inbox_cnt.badge_text = state.inbox_count + msg_counter_objs.trash_cnt.badge_text = state.trash_count + msg_counter_objs.draft_cnt.badge_text = state.draft_count + msg_counter_objs.allmail_cnt.badge_text = state.all_count + + def on_start(self): + self.set_message_count() + @staticmethod def on_stop(): - """On stop methos is used for stoping the runing script.""" - print("*******************EXITING FROM APPLICATION*******************") + """On stop methos is used for stoping the runing script""" + print ("*******************EXITING FROM APPLICATION*******************") import shutdown shutdown.doCleanShutdown() @staticmethod def current_address_label(current_add_label=None, current_addr=None): - """Getting current address labels.""" + """Getting current address labels""" if BMConfigParser().addresses(): if current_add_label: first_name = current_add_label @@ -1196,99 +1611,133 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods addr = BMConfigParser().addresses()[0] first_name = BMConfigParser().get(addr, 'label') f_name = first_name.split() - label = f_name[0][:14].capitalize() + '...' if len(f_name[0]) > 15 else f_name[0].capitalize() + label = f_name[0][:14].capitalize() + '...' if len( + f_name[0]) > 15 else f_name[0].capitalize() address = ' (' + addr + '...)' return label + address return '' def searchQuery(self, instance): - """Method used for showing searched mails.""" + """Showing searched mails""" state.search_screen = self.root.ids.scr_mngr.current state.searcing_text = str(instance.text).strip() - if state.search_screen == 'inbox': - self.root.ids.sc1.ids.ml.clear_widgets() - self.root.ids.sc1.loadMessagelist(state.association) - elif state.search_screen == 'addressbook': - self.root.ids.sc11.ids.ml.clear_widgets() - self.root.ids.sc11.loadAddresslist(None, 'All', '') - elif state.search_screen == 'myaddress': - self.root.ids.sc10.ids.ml.clear_widgets() - self.root.ids.sc10.init_ui() - else: - self.root.ids.sc4.ids.ml.clear_widgets() - self.root.ids.sc4.loadSent(state.association) - self.root.ids.scr_mngr.current = state.search_screen + if instance.focus and state.searcing_text: + toolbar_obj = self.root.ids.toolbar + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.closeSearchScreen()]] + toolbar_obj.right_action_items = [] + self.root.ids.toolbar.title = '' + state.in_search_mode = True - def refreshScreen(self, instance): - """Method show search button only on inbox or sent screen.""" + def closeSearchScreen(self): + """Function for close search screen""" + self.set_common_header() + address_label = self.current_address_label( + BMConfigParser().get( + state.association, 'label'), state.association) + self.root.ids.toolbar.title = address_label state.searcing_text = '' - if instance.text == 'Sent': - self.root.ids.sc4.ids.ml.clear_widgets() - self.root.ids.sc4.children[1].children[1].ids.search_field.text = '' - self.root.ids.sc4.loadSent(state.association) - elif instance.text == 'Inbox': - self.root.ids.sc1.ids.ml.clear_widgets() + self.refreshScreen() + state.in_search_mode = False + + def refreshScreen(self): # pylint: disable=unused-variable + """Method show search button only on inbox or sent screen""" + state.searcing_text = '' + if state.search_screen == 'inbox': try: - self.root.ids.sc1.children[2].children[1].ids.search_field.text = '' - except Exception as e: - self.root.ids.sc1.children[1].children[1].ids.search_field.text = '' - self.root.ids.sc1.loadMessagelist(state.association) - elif instance.text == 'Draft': - self.root.ids.sc16.clear_widgets() - self.root.ids.sc16.add_widget(Draft()) - elif instance.text == 'Trash': - self.root.ids.sc5.clear_widgets() - self.root.ids.sc5.add_widget(Trash()) - elif instance.text == 'All Mails': - self.root.ids.sc17.clear_widgets() - self.root.ids.sc17.add_widget(Allmails()) - elif instance.text == 'Address Book': - self.root.ids.sc11.ids.ml.clear_widgets() - self.root.ids.sc11.children[1].children[1].ids.search_field.text = '' - self.root.ids.sc11.loadAddresslist(None, 'All', '') - elif instance.text == 'My Addresses': - self.root.ids.sc10.ids.ml.clear_widgets() + self.root.ids.sc1.children[ + 3].children[1].ids.search_field.text = '' + except Exception: + self.root.ids.sc1.children[ + 2].children[1].ids.search_field.text = '' + self.root.ids.sc1.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'addressbook': + self.root.ids.sc11.children[ + 2].children[1].ids.search_field.text = '' + self.root.ids.sc11.children[ + 1].active = True + Clock.schedule_once(self.search_callback, 0.5) + elif state.search_screen == 'myaddress': try: - self.root.ids.sc10.children[1].children[1].ids.search_field.text = '' - except Exception as e: - self.root.ids.sc10.children[2].children[1].ids.search_field.text = '' - self.root.ids.sc10.init_ui() + self.root.ids.sc10.children[ + 3].children[1].ids.search_field.text = '' + except Exception: + self.root.ids.sc10.children[ + 2].children[1].ids.search_field.text = '' + self.root.ids.sc10.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) + else: + self.root.ids.sc4.children[ + 2].children[1].ids.search_field.text = '' + self.root.ids.sc4.children[1].active = True + Clock.schedule_once(self.search_callback, 0.5) return def set_identicon(self, text): - """This method is use for showing identicon in address spinner""" + """Show identicon in address spinner""" img = identiconGeneration.generate(text) - self.root.children[2].children[0].ids.btn.children[1].texture = img.texture - - @staticmethod - def address_identicon(): - """Address identicon""" - return './images/drawer_logo1.png' + self.root.children[2].children[0].ids.btn.children[1].texture = ( + img.texture) def set_mail_detail_header(self): - """Method is used for setting the details of the page""" + """Setting the details of the page""" toolbar_obj = self.root.ids.toolbar - toolbar_obj.left_action_items = [['arrow-left', lambda x: self.back_press()]] - delete_btn = ['delete-forever', lambda x: self.root.ids.sc14.delete_mail()] + toolbar_obj.left_action_items = [ + ['arrow-left', lambda x: self.back_press()]] + delete_btn = ['delete-forever', + lambda x: self.root.ids.sc14.delete_mail()] dynamic_list = [] if state.detailPageType == 'inbox': - dynamic_list = [['reply', lambda x: self.root.ids.sc14.inbox_reply()], delete_btn] + dynamic_list = [ + ['reply', lambda x: self.root.ids.sc14.inbox_reply()], + delete_btn] elif state.detailPageType == 'sent': dynamic_list = [delete_btn] elif state.detailPageType == 'draft': - dynamic_list = [['pencil', lambda x: self.root.ids.sc14.write_msg(self)], delete_btn] + dynamic_list = [ + ['pencil', lambda x: self.root.ids.sc14.write_msg(self)], + delete_btn] toolbar_obj.right_action_items = dynamic_list + def load_screen(self, instance): + """This method is used for loading screen on every click""" + if instance.text == 'Inbox': + self.root.ids.scr_mngr.current = 'inbox' + self.root.ids.sc1.children[1].active = True + elif instance.text == 'All Mails': + self.root.ids.scr_mngr.current = 'allmails' + try: + self.root.ids.sc17.children[1].active = True + except Exception as e: + self.root.ids.sc17.children[0].children[1].active = True + Clock.schedule_once(partial(self.load_screen_callback, instance), 1) + + def load_screen_callback(self, instance, dt=0): + """This method is rotating loader for few seconds""" + if instance.text == 'Inbox': + self.root.ids.sc1.ids.ml.clear_widgets() + self.root.ids.sc1.loadMessagelist(state.association) + self.root.ids.sc1.children[1].active = False + elif instance.text == 'All Mails': + self.root.ids.sc17.clear_widgets() + self.root.ids.sc17.add_widget(Allmails()) + try: + self.root.ids.sc17.children[1].active = False + except Exception as e: + self.root.ids.sc17.children[0].children[1].active = False + class GrashofPopup(Popup): - """Methods for saving contacts, error messages.""" + """Moule for save contacts and error messages""" + valid = False - def __init__(self, **kwargs): # pylint: disable=useless-super-delegation - """Grash of pop screen settings.""" + def __init__(self, **kwargs): # pylint: disable=useless-super-delegation + """Grash of pop screen settings""" super(GrashofPopup, self).__init__(**kwargs) def savecontact(self): - """Method is used for Saving Contacts.""" + """Method is used for saving contacts""" label = self.ids.label.text.strip() address = self.ids.address.text.strip() if label == '' and address == '': @@ -1301,19 +1750,22 @@ class GrashofPopup(Popup): stored_address = [addr[1] for addr in kivy_helper_search.search_sql( folder="addressbook")] - if label and address and address not in stored_address: + stored_labels = [labels[0] for labels in kivy_helper_search.search_sql( + folder="addressbook")] + if label and address and address not in stored_address \ + and label not in stored_labels and self.valid: # state.navinstance = self.parent.children[1] queues.UISignalQueue.put(('rerenderAddressBook', '')) self.dismiss() sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) - state.kivyapp.root.ids.sc11.ids.ml.clear_widgets() - state.kivyapp.root.ids.sc11.loadAddresslist(None, 'All', '') + self.parent.children[1].ids.sc11.ids.ml.clear_widgets() + self.parent.children[1].ids.sc11.loadAddresslist(None, 'All', '') self.parent.children[1].ids.scr_mngr.current = 'addressbook' toast('Saved') # pylint: disable=attribute-defined-outside-init def show_error_message(self): - """Showing error message.""" + """Showing error message""" content = MDLabel( font_style='Body1', theme_text_color='Secondary', @@ -1333,12 +1785,13 @@ class GrashofPopup(Popup): @staticmethod def close_pop(): - """Pop is Canceled.""" + """Pop is Canceled""" toast('Canceled') def checkAddress_valid(self, instance): - """Checking is address is valid or not""" - my_addresses = self.parent.children[1].children[2].children[0].ids.btn.values + """Checking address is valid or not""" + my_addresses = ( + self.parent.children[1].children[2].children[0].ids.btn.values) add_book = [addr[1] for addr in kivy_helper_search.search_sql( folder="addressbook")] entered_text = str(instance.text).strip() @@ -1346,20 +1799,61 @@ class GrashofPopup(Popup): text = 'Address is already in the addressbook.' elif entered_text in my_addresses: text = 'You can not save your own address.' + elif entered_text: + text = self.addressChanged(entered_text) if entered_text in my_addresses or entered_text in add_book: - if len(self.ids.popup_box.children) <= 2: - err_msg = MDLabel( - id='erro_msg', - font_style='Body2', - text=text, - font_size=5) - err_msg.color = 1, 0, 0, 1 - self.ids.popup_box.add_widget(err_msg) - self.ids.save_addr.disabled = True - elif len(self.ids.popup_box.children) > 2: - self.ids.popup_box.remove_widget(self.ids.popup_box.children[0]) - self.ids.save_addr.disabled = False + self.ids.address.error = True + self.ids.address.helper_text = text + elif entered_text and self.valid: + self.ids.address.error = False + elif entered_text: + self.ids.address.error = True + self.ids.address.helper_text = text + else: + self.ids.address.error = False + self.ids.address.helper_text = 'This field is required' + + def checkLabel_valid(self, instance): + """Checking address label is unique or not""" + entered_label = instance.text.strip() + addr_labels = [labels[0] for labels in kivy_helper_search.search_sql( + folder="addressbook")] + if entered_label in addr_labels: + self.ids.label.error = True + self.ids.label.helper_text = 'label name already exists.' + elif entered_label: + self.ids.label.error = False + else: + self.ids.label.error = False + self.ids.label.helper_text = 'This field is required' + + def _onSuccess(self, addressVersion, streamNumber, ripe): + pass + + def addressChanged(self, addr): + """Address validation callback, performs validation and gives feedback""" + status, addressVersion, streamNumber, ripe = decodeAddress( + str(addr)) + self.valid = status == 'success' + if self.valid: + text = "Address is valid." + self._onSuccess(addressVersion, streamNumber, ripe) + elif status == 'missingbm': + text = "The address should start with ''BM-''" + elif status == 'checksumfailed': + text = "The address is not typed or copied correctly(the checksum failed)." + elif status == 'versiontoohigh': + text = "The version number of this address is higher than this software can support. Please upgrade Bitmessage." + elif status == 'invalidcharacters': + text = "The address contains invalid characters." + elif status == 'ripetooshort': + text = "Some data encoded in the address is too short." + elif status == 'ripetoolong': + text = "Some data encoded in the address is too long." + elif status == 'varintmalformed': + text = "Some data encoded in the address is malformed." + return text class AvatarSampleWidget(ILeftBody, Image): @@ -1407,8 +1901,7 @@ class NavigationDrawerTwoLineListItem( class MailDetail(Screen): - """MailDetail Screen uses to show the detail of mails.""" - + """MailDetail Screen uses to show the detail of mails""" to_addr = StringProperty() from_addr = StringProperty() subject = StringProperty() @@ -1417,30 +1910,30 @@ class MailDetail(Screen): page_type = StringProperty() def __init__(self, *args, **kwargs): - """Mail Details method.""" + """Mail Details method""" super(MailDetail, self).__init__(*args, **kwargs) Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method MailDetail mails.""" + """Clock Schdule for method MailDetail mails""" self.page_type = state.detailPageType if state.detailPageType else '' if state.detailPageType == 'sent' or state.detailPageType == 'draft': data = sqlQuery( - "select toaddress, fromaddress, subject, message, status, \ - ackdata from sent where ackdata = ?;", state.mail_id) + "select toaddress, fromaddress, subject, message, status," + " ackdata from sent where ackdata = ?;", state.mail_id) state.status = self state.ackdata = data[0][5] self.assign_mail_details(data) state.kivyapp.set_mail_detail_header() elif state.detailPageType == 'inbox': data = sqlQuery( - "select toaddress, fromaddress, subject, message from inbox \ - where msgid = ?;", str(state.mail_id)) + "select toaddress, fromaddress, subject, message from inbox" + " where msgid = ?;", str(state.mail_id)) self.assign_mail_details(data) state.kivyapp.set_mail_detail_header() def assign_mail_details(self, data): - """Assigning mail details.""" + """Assigning mail details""" self.to_addr = data[0][0] self.from_addr = data[0][1] self.subject = data[0][2].upper( @@ -1454,80 +1947,99 @@ class MailDetail(Screen): 'message': self.message} def delete_mail(self): - """Method for mail delete.""" + """Method for mail delete""" msg_count_objs = state.kivyapp.root.children[2].children[0].ids + state.searcing_text = '' + self.children[0].children[0].active = True if state.detailPageType == 'sent': + state.kivyapp.root.ids.sc4.children[ + 2].children[1].ids.search_field.text = '' sqlExecute( - "UPDATE sent SET folder = 'trash' WHERE \ - ackdata = ?;", str(state.mail_id)) + "UPDATE sent SET folder = 'trash' WHERE" + " ackdata = ?;", str(state.mail_id)) msg_count_objs.send_cnt.badge_text = str(int(state.sent_count) - 1) state.sent_count = str(int(state.sent_count) - 1) self.parent.screens[3].ids.ml.clear_widgets() self.parent.screens[3].loadSent(state.association) elif state.detailPageType == 'inbox': + state.kivyapp.root.ids.sc1.children[ + 2].children[1].ids.search_field.text = '' + self.parent.screens[0].children[2].children[ + 1].ids.search_field.text = '' sqlExecute( - "UPDATE inbox SET folder = 'trash' WHERE \ - msgid = ?;", str(state.mail_id)) - msg_count_objs.inbox_cnt.badge_text = str(int(state.inbox_count) - 1) + "UPDATE inbox SET folder = 'trash' WHERE" + " msgid = ?;", str(state.mail_id)) + msg_count_objs.inbox_cnt.badge_text = str( + int(state.inbox_count) - 1) state.inbox_count = str(int(state.inbox_count) - 1) self.parent.screens[0].ids.ml.clear_widgets() self.parent.screens[0].loadMessagelist(state.association) + elif state.detailPageType == 'draft': sqlExecute("DELETE FROM sent WHERE ackdata = ?;", str( state.mail_id)) - msg_count_objs.draft_cnt.badge_text = str(int(state.draft_count) - 1) + msg_count_objs.draft_cnt.badge_text = str( + int(state.draft_count) - 1) state.draft_count = str(int(state.draft_count) - 1) self.parent.screens[15].clear_widgets() self.parent.screens[15].add_widget(Draft()) - self.parent.current = 'allmails' if state.is_allmail else state.detailPageType + # self.parent.current = 'allmails' \ + # if state.is_allmail else state.detailPageType if state.detailPageType != 'draft': - msg_count_objs.trash_cnt.badge_text = str(int(state.trash_count) + 1) - msg_count_objs.allmail_cnt.badge_text = str(int(state.all_count) - 1) + msg_count_objs.trash_cnt.badge_text = str( + int(state.trash_count) + 1) + msg_count_objs.allmail_cnt.badge_text = str( + int(state.all_count) - 1) state.trash_count = str(int(state.trash_count) + 1) state.all_count = str(int(state.all_count) - 1) - self.parent.screens[4].clear_widgets() - self.parent.screens[4].add_widget(Trash()) - self.parent.screens[16].clear_widgets() - self.parent.screens[16].add_widget(Allmails()) - state.kivyapp.back_press() + self.parent.screens[4].ids.ml.clear_widgets() + self.parent.screens[4].init_ui(dt=0) + self.parent.screens[16].ids.ml.clear_widgets() + self.parent.screens[16].init_ui(dt=0) + Clock.schedule_once(self.callback_for_delete, 4) + + def callback_for_delete(self, dt=0): + """Delete method from allmails""" + self.children[0].children[0].active = False + state.kivyapp.set_common_header() + self.parent.current = 'allmails' \ + if state.is_allmail else state.detailPageType + state.detailPageType = '' toast('Deleted') def inbox_reply(self): - """Method used for replying inbox messages.""" + """Reply inbox messages""" data = sqlQuery( - "select toaddress, fromaddress, subject, message from inbox where \ - msgid = ?;", str(state.mail_id)) - composer_obj = self.parent.screens[2].children[0].ids + "select toaddress, fromaddress, subject, message from inbox where" + " msgid = ?;", str(state.mail_id)) + composer_obj = self.parent.screens[2].children[1].ids composer_obj.ti.text = data[0][0] composer_obj.btn.text = data[0][0] composer_obj.txt_input.text = data[0][1] composer_obj.subject.text = data[0][2] composer_obj.body.text = '' - state.kivyapp.root.ids.sc3.children[0].ids.rv.data = '' + state.kivyapp.root.ids.sc3.children[1].ids.rv.data = '' self.parent.current = 'create' state.kivyapp.set_navbar_for_composer() - def copy_sent_mail(self): - """Method used for copying sent mail to the composer.""" - pass - def write_msg(self, navApp): - """Method used to write on draft mail.""" + """Write on draft mail""" state.send_draft_mail = state.mail_id - composer_ids = \ - self.parent.parent.parent.parent.ids.sc3.children[0].ids + composer_ids = ( + self.parent.parent.parent.parent.parent.ids.sc3.children[1].ids) composer_ids.ti.text = state.write_msg['from_addr'] composer_ids.btn.text = state.write_msg['from_addr'] composer_ids.txt_input.text = state.write_msg['to_addr'] - composer_ids.subject.text = state.write_msg['subject'] + composer_ids.subject.text = state.write_msg[ + 'subject'] if state.write_msg['subject'] != '(no subject)' else '' composer_ids.body.text = state.write_msg['message'] self.parent.current = 'create' navApp.set_navbar_for_composer() @staticmethod def copy_composer_text(instance, *args): - """This method is used for copying the data from mail detail page""" + """Copy the data from mail detail page""" if len(instance.parent.text.split(':')) > 1: cpy_text = instance.parent.text.split(':')[1].strip() else: @@ -1537,78 +2049,103 @@ class MailDetail(Screen): class MyaddDetailPopup(Popup): - """MyaddDetailPopup pop is used for showing my address detail.""" - + """MyaddDetailPopup pop is used for showing my address detail""" address_label = StringProperty() address = StringProperty() - def __init__(self, **kwargs): # pylint: disable=useless-super-delegation - """My Address Details screen setting.""" + def __init__(self, **kwargs): # pylint: disable=useless-super-delegation + """My Address Details screen setting""" super(MyaddDetailPopup, self).__init__(**kwargs) def set_address(self, address, label): - """Getting address for displaying details on popup.""" + """Getting address for displaying details on popup""" self.address_label = label self.address = address def send_message_from(self): - """Method used to fill from address of composer autofield.""" + """Method used to fill from address of composer autofield""" + state.kivyapp.set_navbar_for_composer() window_obj = self.parent.children[1].ids - window_obj.sc3.children[0].ids.ti.text = self.address - window_obj.sc3.children[0].ids.btn.text = self.address - window_obj.sc3.children[0].ids.txt_input.text = '' - window_obj.sc3.children[0].ids.subject.text = '' - window_obj.sc3.children[0].ids.body.text = '' + window_obj.sc3.children[1].ids.ti.text = self.address + window_obj.sc3.children[1].ids.btn.text = self.address + window_obj.sc3.children[1].ids.txt_input.text = '' + window_obj.sc3.children[1].ids.subject.text = '' + window_obj.sc3.children[1].ids.body.text = '' window_obj.scr_mngr.current = 'create' self.dismiss() @staticmethod def close_pop(): - """Pop is Canceled.""" + """Pop is Canceled""" toast('Canceled') class AddbookDetailPopup(Popup): - """AddbookDetailPopup pop is used for showing my address detail.""" - + """AddbookDetailPopup pop is used for showing my address detail""" address_label = StringProperty() address = StringProperty() - def __init__(self, **kwargs): # pylint: disable=useless-super-delegation - """Method used set screen of address detail page.""" + def __init__(self, **kwargs): + """Set screen of address detail page""" + # pylint: disable=useless-super-delegation super(AddbookDetailPopup, self).__init__(**kwargs) def set_addbook_data(self, address, label): - """Getting address book data for detial dipaly.""" + """Getting address book data for detial dipaly""" self.address_label = label self.address = address def update_addbook_label(self, address): - """Updating the label of address book address.""" - if str(self.ids.add_label.text): - sqlExecute("UPDATE addressbook SET label = '{}' WHERE \ - address = '{}';".format(str(self.ids.add_label.text), address)) + """Updating the label of address book address""" + address_list = kivy_helper_search.search_sql(folder="addressbook") + stored_labels = [labels[0] for labels in address_list] + add_dict = dict(address_list) + label = str(self.ids.add_label.text) + if label in stored_labels and self.address == add_dict[label]: + stored_labels.remove(label) + if label and label not in stored_labels: + sqlExecute( + "UPDATE addressbook SET label = '{}' WHERE" + " address = '{}';".format( + str(self.ids.add_label.text), address)) self.parent.children[1].ids.sc11.ids.ml.clear_widgets() self.parent.children[1].ids.sc11.loadAddresslist(None, 'All', '') self.dismiss() toast('Saved') def send_message_to(self): - """Method used to fill to_address of composer autofield.""" + """Method used to fill to_address of composer autofield""" + state.kivyapp.set_navbar_for_composer() window_obj = self.parent.children[1].ids - window_obj.sc3.children[0].ids.txt_input.text = self.address - window_obj.sc3.children[0].ids.ti.text = '' - window_obj.sc3.children[0].ids.btn.text = 'Select' - window_obj.sc3.children[0].ids.subject.text = '' - window_obj.sc3.children[0].ids.body.text = '' + window_obj.sc3.children[1].ids.txt_input.text = self.address + window_obj.sc3.children[1].ids.ti.text = '' + window_obj.sc3.children[1].ids.btn.text = 'Select' + window_obj.sc3.children[1].ids.subject.text = '' + window_obj.sc3.children[1].ids.body.text = '' window_obj.scr_mngr.current = 'create' self.dismiss() @staticmethod def close_pop(): - """Pop is Canceled.""" + """Pop is Canceled""" toast('Canceled') + def checkLabel_valid(self, instance): + """Checking address label is unique of not""" + entered_label = str(instance.text.strip()) + address_list = kivy_helper_search.search_sql(folder="addressbook") + addr_labels = [labels[0] for labels in address_list] + add_dict = dict(address_list) + if self.address and entered_label in addr_labels \ + and self.address != add_dict[entered_label]: + self.ids.add_label.error = True + self.ids.add_label.helper_text = 'label name already exists.' + elif entered_label: + self.ids.add_label.error = False + else: + self.ids.add_label.error = False + self.ids.add_label.helper_text = 'This field is required' + class ShowQRCode(Screen): """ShowQRCode Screen uses to show the detail of mails.""" @@ -1624,12 +2161,14 @@ class ShowQRCode(Screen): class Draft(Screen): - """Draft screen is used to show the list of draft messages.""" - + """Draft screen is used to show the list of draft messages""" data = ListProperty() + account = StringProperty() + queryreturn = ListProperty() + has_refreshed = True def __init__(self, *args, **kwargs): - """Method used for storing draft messages.""" + """Method used for storing draft messages""" super(Draft, self).__init__(*args, **kwargs) if state.association == '': if BMConfigParser().addresses(): @@ -1637,60 +2176,27 @@ class Draft(Screen): Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method draft accounts.""" + """Clock Schdule for method draft accounts""" self.sentaccounts() - print (dt) + print(dt) def sentaccounts(self): """Load draft accounts.""" - account = state.association - self.loadDraft(account, 'All', '') + self.account = state.association + self.loadDraft() - def loadDraft(self, account, where="", what=""): + def loadDraft(self, where="", what=""): """Load draft list for Draft messages.""" xAddress = 'fromaddress' - queryreturn = kivy_helper_search.search_sql( - xAddress, account, "draft", where, what, False) + self.draftDataQuery(xAddress, where, what) if state.msg_counter_objs: - state.msg_counter_objs.draft_cnt.badge_text = str(len(queryreturn)) - if queryreturn: + state.msg_counter_objs.draft_cnt.badge_text = str( + len(self.queryreturn)) + if self.queryreturn: src_mng_obj = state.kivyapp.root.children[2].children[0].ids - src_mng_obj.draft_cnt.badge_text = str(len(queryreturn)) - state.draft_count = str(len(queryreturn)) - for mail in queryreturn: - third_text = mail[3].replace('\n', ' ') - self.data.append({ - 'text': mail[1].strip(), - 'secondary_text': mail[2][:10] + '...........' if len( - mail[2]) > 10 else mail[2] + '\n' + " " + ( - third_text[:25] + '...!') if len( - third_text) > 25 else third_text, - 'ackdata': mail[5]}) - for item in self.data: - meny = TwoLineAvatarIconListItem( - text='Draft', - secondary_text=item['text'], - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/avatar.png')) - meny.bind(on_press=partial( - self.draft_detail, item['ackdata'])) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial( - self.delete_draft, item['ackdata'])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + src_mng_obj.draft_cnt.badge_text = state.draft_count + self.set_mdList() + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -1701,8 +2207,76 @@ class Draft(Screen): valign='top') self.ids.ml.add_widget(content) + # pylint: disable=too-many-arguments + def draftDataQuery(self, xAddress, where, what, start_indx=0, end_indx=20): + """This methosd is for retrieving draft messages""" + self.queryreturn = kivy_helper_search.search_sql( + xAddress, + self.account, + "draft", + where, + what, + False, + start_indx, + end_indx) + + def set_mdList(self): + """This method is used to create mdlist""" + data = [] + total_draft_msg = len(self.ids.ml.children) + for mail in self.queryreturn: + third_text = mail[3].replace('\n', ' ') + data.append({ + 'text': mail[1].strip(), + 'secondary_text': mail[2][:10] + '...........' if len( + mail[2]) > 10 else mail[2] + '\n' + " " + ( + third_text[:25] + '...!') if len( + third_text) > 25 else third_text, + 'ackdata': mail[5]}) + for item in data: + meny = TwoLineAvatarIconListItem( + text='Draft', + secondary_text=item['text'], + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget(AvatarSampleWidget( + source='./images/avatar.png')) + meny.bind(on_press=partial( + self.draft_detail, item['ackdata'])) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind(on_press=partial( + self.delete_draft, item['ackdata'])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + carousel.index = 1 + self.ids.ml.add_widget(carousel) + updated_msg = len(self.ids.ml.children) + self.has_refreshed = True if total_draft_msg != updated_msg else False + + def check_scroll_y(self, instance, somethingelse): + """Load data on scroll""" + if self.ids.scroll_y.scroll_y <= -0.0 and self.has_refreshed: + self.ids.scroll_y.scroll_y = 0.06 + total_draft_msg = len(self.ids.ml.children) + self.update_draft_screen_on_scroll(total_draft_msg) + else: + pass + + def update_draft_screen_on_scroll(self, total_draft_msg, where='', what=''): + """Load more data on scroll down""" + self.draftDataQuery('fromaddress', where, what, total_draft_msg, 5) + self.set_mdList() + def draft_detail(self, ackdata, *args): - """Method used to show draft Details.""" + """Show draft Details""" state.detailPageType = 'draft' state.mail_id = ackdata if self.manager: @@ -1714,15 +2288,17 @@ class Draft(Screen): src_mng_obj.current = 'mailDetail' def delete_draft(self, data_index, instance, *args): - """Method used to delete draft message permanently.""" + """Delete draft message permanently""" sqlExecute("DELETE FROM sent WHERE ackdata = ?;", str( data_index)) try: - msg_count_objs = \ - self.parent.parent.parent.parent.children[2].children[0].ids + msg_count_objs = ( + self.parent.parent.parent.parent.parent.children[2].children[0].ids) except Exception: - msg_count_objs = self.parent.parent.parent.parent.parent.children[ + msg_count_objs = self.parent.parent.parent.parent.parent.parent.children[ 2].children[0].ids + # msg_count_objs = self.parent.parent.parent.parent.parent.children[ + # 2].children[0].ids if int(state.draft_count) > 0: msg_count_objs.draft_cnt.badge_text = str( int(state.draft_count) - 1) @@ -1731,11 +2307,9 @@ class Draft(Screen): toast('Deleted') @staticmethod - def draft_msg(src_object): - """Method used for saving draft mails.""" - # pylint: disable=too-many-locals - composer_object = src_object.children[1].children[0].children[ - 0].children[0].children[0].ids + def draft_msg(src_object): # pylint: disable=too-many-locals + """Save draft mails""" + composer_object = state.kivyapp.root.ids.sc3.children[1].ids fromAddress = str(composer_object.ti.text) toAddress = str(composer_object.txt_input.text) subject = str(composer_object.subject.text) @@ -1744,16 +2318,13 @@ class Draft(Screen): sendMessageToPeople = True if sendMessageToPeople: from addresses import decodeAddress - status, addressVersionNumber, streamNumber, ripe = \ - decodeAddress(toAddress) + streamNumber, ripe = decodeAddress(toAddress)[2:] from addresses import addBMIfNotPresent toAddress = addBMIfNotPresent(toAddress) - statusIconColor = 'red' stealthLevel = BMConfigParser().safeGetInt( 'bitmessagesettings', 'ackstealthlevel') from helper_ackPayload import genAckPayload ackdata = genAckPayload(streamNumber, stealthLevel) - t = () sqlExecute( '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', @@ -1771,7 +2342,8 @@ class Draft(Screen): 0, 'draft', encoding, - BMConfigParser().getint('bitmessagesettings', 'ttl')) + int(BMConfigParser().safeGet( + 'bitmessagesettings', 'ttl'))) state.msg_counter_objs = src_object.children[2].children[0].ids state.draft_count = str(int(state.draft_count) + 1) @@ -1791,9 +2363,11 @@ class CustomSpinner(Spinner): class Allmails(Screen): - """all mails Screen uses screen to show widgets of screens.""" - + """All mails Screen uses screen to show widgets of screens""" data = ListProperty() + has_refreshed = True + all_mails = ListProperty() + account = StringProperty() def __init__(self, *args, **kwargs): """Method Parsing the address.""" @@ -1804,53 +2378,24 @@ class Allmails(Screen): Clock.schedule_once(self.init_ui, 0) def init_ui(self, dt=0): - """Clock Schdule for method all mails.""" - self.mailaccounts() + """Clock Schdule for method all mails""" + self.loadMessagelist() print (dt) - def mailaccounts(self): - """Load all mails for account.""" - account = state.association - self.loadMessagelist(account, 'All', '') - - def loadMessagelist(self, account, where="", what=""): + def loadMessagelist(self): """Load Inbox, Sent anf Draft list of messages.""" - all_mails = sqlQuery( - "SELECT toaddress, fromaddress, subject, message, folder, ackdata As id, DATE(lastactiontime)" - " As actionTime FROM sent WHERE folder = 'sent' UNION" - " SELECT toaddress, fromaddress, subject, message, folder, msgid As id, DATE(received) As" - " actionTime FROM inbox WHERE folder = 'inbox' ORDER BY actionTime DESC") - if all_mails: - state.kivyapp.root.children[2].children[0].ids.allmail_cnt.badge_text = str(len(all_mails)) - state.all_count = str(len(all_mails)) - for item in all_mails: - meny = TwoLineAvatarIconListItem( - text=item[1], - secondary_text=item[2][:50] + '........' if len( - item[2]) >= 50 else ( - item[2] + ',' + item[3].replace('\n', ''))[0:50] + '........', - theme_text_color='Custom', - text_color=NavigateApp().theme_cls.primary_color) - meny.add_widget(AvatarSampleWidget( - source='./images/text_images/{}.png'.format( - avatarImageFirstLetter(item[2].strip())))) - meny.bind(on_press=partial( - self.mail_detail, item[5], item[4])) - carousel = Carousel(direction='right') - carousel.height = meny.height - carousel.size_hint_y = None - carousel.ignore_perpendicular_swipes = True - carousel.data_index = 0 - carousel.min_move = 0.2 - del_btn = Button(text='Delete') - del_btn.background_normal = '' - del_btn.background_color = (1, 0, 0, 1) - del_btn.bind(on_press=partial( - self.swipe_delete, item[5], item[4])) - carousel.add_widget(del_btn) - carousel.add_widget(meny) - carousel.index = 1 - self.ids.ml.add_widget(carousel) + self.account = state.association + self.allMessageQuery(0, 20) + if self.all_mails: + state.kivyapp.get_inbox_count() + state.kivyapp.get_sent_count() + state.all_count = str( + int(state.sent_count) + int(state.inbox_count)) + state.kivyapp.root.children[2].children[ + 0].ids.allmail_cnt.badge_text = state.all_count + self.set_mdlist() + # self.ids.refresh_layout.bind(scroll_y=self.check_scroll_y) + self.ids.scroll_y.bind(scroll_y=self.check_scroll_y) else: content = MDLabel( font_style='Caption', @@ -1861,8 +2406,70 @@ class Allmails(Screen): valign='top') self.ids.ml.add_widget(content) + def allMessageQuery(self, start_indx, end_indx): + """Retrieving data from inbox or sent both tables""" + self.all_mails = sqlQuery( + "SELECT toaddress, fromaddress, subject, message, folder, ackdata" + " As id, DATE(lastactiontime) As actionTime FROM sent WHERE" + " folder = 'sent' and fromaddress = '{0}'" + " UNION SELECT toaddress, fromaddress, subject, message, folder," + " msgid As id, DATE(received) As actionTime FROM inbox" + " WHERE folder = 'inbox' and toaddress = '{0}'" + " ORDER BY actionTime DESC limit {1}, {2}".format( + self.account, start_indx, end_indx)) + + def set_mdlist(self): + """This method is used to create mdList for allmaills""" + data_exist = len(self.ids.ml.children) + for item in self.all_mails: + meny = TwoLineAvatarIconListItem( + text=item[1], + secondary_text=item[2][:50] + '........' if len( + item[2]) >= 50 else ( + item[2] + ',' + item[3].replace( + '\n', ''))[0:50] + '........', + theme_text_color='Custom', + text_color=NavigateApp().theme_cls.primary_color) + meny.add_widget(AvatarSampleWidget( + source='./images/text_images/{}.png'.format( + avatarImageFirstLetter(item[2].strip())))) + meny.bind(on_press=partial( + self.mail_detail, item[5], item[4])) + carousel = Carousel(direction='right') + carousel.height = meny.height + carousel.size_hint_y = None + carousel.ignore_perpendicular_swipes = True + carousel.data_index = 0 + carousel.min_move = 0.2 + del_btn = Button(text='Delete') + del_btn.background_normal = '' + del_btn.background_color = (1, 0, 0, 1) + del_btn.bind(on_press=partial( + self.swipe_delete, item[5], item[4])) + carousel.add_widget(del_btn) + carousel.add_widget(meny) + carousel.index = 1 + self.ids.ml.add_widget(carousel) + updated_data = len(self.ids.ml.children) + self.has_refreshed = True if data_exist != updated_data else False + + def check_scroll_y(self, instance, somethingelse): + """Scroll fixed length""" + if self.ids.scroll_y.scroll_y <= -0.00 and self.has_refreshed: + self.ids.scroll_y.scroll_y = .06 + load_more = len(self.ids.ml.children) + self.updating_allmail(load_more) + else: + pass + + def updating_allmail(self, load_more): + """This method is used to update the all mail + listing value on the scroll of screen""" + self.allMessageQuery(load_more, 5) + self.set_mdlist() + def mail_detail(self, unique_id, folder, *args): - """Load sent and inbox mail details.""" + """Load sent and inbox mail details""" state.detailPageType = folder state.is_allmail = True state.mail_id = unique_id @@ -1875,7 +2482,7 @@ class Allmails(Screen): src_mng_obj.current = 'mailDetail' def swipe_delete(self, unique_id, folder, instance, *args): - """Delete inbox mail from all mail listing listing.""" + """Delete inbox mail from all mail listing""" if folder == 'inbox': sqlExecute( "UPDATE inbox SET folder = 'trash' WHERE msgid = ?;", str( @@ -1886,13 +2493,13 @@ class Allmails(Screen): unique_id)) self.ids.ml.remove_widget(instance.parent.parent) try: - msg_count_objs = self.parent.parent.parent.parent.children[ - 2].children[0].ids - nav_lay_obj = self.parent.parent.parent.parent.ids - except Exception: msg_count_objs = self.parent.parent.parent.parent.parent.children[ 2].children[0].ids nav_lay_obj = self.parent.parent.parent.parent.parent.ids + except Exception: + msg_count_objs = self.parent.parent.parent.parent.parent.parent.children[ + 2].children[0].ids + nav_lay_obj = self.parent.parent.parent.parent.parent.parent.ids if folder == 'inbox': msg_count_objs.inbox_cnt.badge_text = str( int(state.inbox_count) - 1) @@ -1913,14 +2520,14 @@ class Allmails(Screen): state.all_count = str(int(state.all_count) - 1) nav_lay_obj.sc5.clear_widgets() nav_lay_obj.sc5.add_widget(Trash()) - nav_lay_obj.sc17.clear_widgets() - nav_lay_obj.sc17.add_widget(Allmails()) + nav_lay_obj.sc17.remove_widget(instance.parent.parent) # pylint: disable=attribute-defined-outside-init def refresh_callback(self, *args): - """Method updates the state of application, While the spinner remains on the screen.""" + """Method updates the state of application, + While the spinner remains on the screen""" def refresh_callback(interval): - """Method used for loading the allmails screen data.""" + """Load the allmails screen data""" self.ids.ml.clear_widgets() self.remove_widget(self.children[1]) try: @@ -1932,16 +2539,25 @@ class Allmails(Screen): self.tick = 0 Clock.schedule_once(refresh_callback, 1) + def set_root_layout(self): + """Setting root layout""" + try: + return self.manager.parent.parent + except Exception: + return state.kivyapp.root.ids.float_box + def avatarImageFirstLetter(letter_string): - """This method is used to the first letter for the avatar image""" - if letter_string[0].upper() >= 'A' and letter_string[0].upper() <= 'Z': - img_latter = letter_string[0].upper() - elif int(letter_string[0]) >= 0 and int(letter_string[0]) <= 9: - img_latter = letter_string[0] + """This function is used to the first letter for the avatar image""" + if letter_string: + if letter_string[0].upper() >= 'A' and letter_string[0].upper() <= 'Z': + img_latter = letter_string[0].upper() + elif int(letter_string[0]) >= 0 and int(letter_string[0]) <= 9: + img_latter = letter_string[0] + else: + img_latter = '!' else: img_latter = '!' - return img_latter @@ -1961,3 +2577,16 @@ class Spam(Screen): """Spam Screen show widgets of page.""" pass + + +class LoadingPopup(Popup): + """Class for loading Popup""" + + def __init__(self, **kwargs): + super(LoadingPopup, self).__init__(**kwargs) + # call dismiss_popup in 2 seconds + Clock.schedule_once(self.dismiss_popup, 0.5) + + def dismiss_popup(self, dt): + """Dismiss popups""" + self.dismiss() diff --git a/src/images/loader.zip b/src/images/loader.zip new file mode 100644 index 0000000000000000000000000000000000000000..c80a3a17e22d05832db089959679ec1c995e0cb9 GIT binary patch literal 15918 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 K`X3bl!2bc@Mkc@j literal 0 HcmV?d00001 diff --git a/src/state.py b/src/state.py index 4baf5614..54251270 100644 --- a/src/state.py +++ b/src/state.py @@ -106,3 +106,7 @@ in_composer = False write_msg = {} availabe_credit = 0 + +in_sent_method = False + +in_search_mode = False \ No newline at end of file