import asyncio from izzylib.misc import random_str from .. import cache, var from ..base import ComponentBase from ..database import default_permissions from ..exceptions import AccountNotFoundError, NoAccountsError from ..functions import connect, get_buffer_text from ..objects import SavedLoginRow SITEOPTIONS = [ 'instance', 'adblock', 'fullscreen', 'autoplay', 'dialog', 'notification', 'microphone', 'location', 'camera' ] class StatusBar(ComponentBase): def __init__(self, window): ComponentBase.__init__(self, 'statusbar') self.window = window self.siteoptions_handler_ids = [] self.bookmark_row = None self.theme_enabled = False self.toot_acct = None self.toot_post = None self.toot_max_len = 500 self.unsaved_logins = [] self.setup() @property def tab(self): return self.window.active_tab def bookmark_get_data(self): data = DotDict() for k,v in self.bookmark_fields.items(): if k == 'description': data[k] = get_buffer_text(v.get_buffer()) else: data[k] = v.get_text() return data def bookmark_refresh_categories(self): menu = self['bookmark-category-menu'] menu.foreach(lambda child: child.destroy()) with self.db.session as s: for category in s.get_categories(): item = Gtk.MenuItem.new_with_label(category) item.show() connect(item, 'activate', self.handle_bookmark_set_category, category) menu.add(item) def bookmark_set_data(self, name=None, url=None, category=None, description=None): data = dict(name=name, url=url, category=category, description=description) for k,v in data.items(): value = v if v != None else '' if k == 'description': self.bookmark_fields[k].get_buffer().set_text(value) else: self.bookmark_fields[k].set_text(value) def login_unsaved_del_row(self, row): self.logins.unsaved.remove(row['container']) self.unsaved_logins.remove(row) def login_unsaved_get_row(self, widget): for row in self.unsaved_logins: if widget == row['container']: return row def login_unsaved_new(self, data): self.logins.unsaved.add(SavedLoginRow(data)) def login_unsaved_refresh(self): for child in self.logins.unsaved.get_children(): self.logins.unsaved.remove(child) child.destroy() for row in self.unsaved_logins: self.logins.unsaved(UnsavedLoginRow(row)['container']) def toot_close(self, *args): self['toot-popover'].popdown() def toot_clear(self, *args): self.toot_fields.spoiler.set_text('') self.toot_fields.content.get_buffer().set_text('') self.toot_fields.visibility.set_active(0) self.toot_update_count() self.toot_set_reply() def toot_set_account(self): try: self.toot_account = self.app.get_default_account() if self.toot_account.avatar.exists(): avatar = GdkPixbuf.Pixbuf.new_from_file_at_scale(self.toot_account.avatar, -1, 24, True) self['toot-avatar'].set_from_pixbuf(avatar) self['toot-avatar'].set_tooltip_text(f'{self.toot_account.data.display_name} ({self.toot_account.fullhandle})') except NoAccountsError: self.window.notification('No active fedi accounts', 'error') return False return True def toot_get_info(self): return DotDict( spoiler = self.toot_fields.spoiler.get_text(), content = get_buffer_text(self.toot_fields.content.get_buffer()), visibility = self.toot_fields.visibility.get_active_text() ) def toot_send(self, *args): data = self.toot_get_info() options = { 'visibility': data.visibility.lower() } if not (data.spoiler or data.content): return self.window.notification('Unwilling to send empty post', level='error') if data.spoiler: options['spoiler_text'] = data.spoiler if self['toot-reply-scroll'].get_visible(): options['in_reply_to_id'] = self.toot_post.id try: self.toot_account.api.status_post(data.content, **options) except Exception as e: logging.error(f'{type(e).__name__}: {e}') self.window.notification('Failed to send toot') return self.window.notification('Sent toot') self['toot-popover'].popdown() self.toot_clear() def toot_set_reply(self, toot=None): self.toot_post = toot reply = self.toot_fields.reply scroll = self['toot-reply-scroll'] privacy = self['toot-visibility'] account = self.toot_post.get('account') if toot else None if not toot and not account: scroll.hide() return text_buffer = reply.get_buffer() text_buffer.set_text(f'Reply to: {account.display_name} ({account.acct}):\n\n{toot.content}') reply.set_buffer(text_buffer) privacy.set_active_id(toot.get('visibility', 'public')) scroll.show() def toot_update_count(self, *args): spoiler = len(self.toot_fields.spoiler.get_text()) content = self.toot_fields.content.get_buffer().get_char_count() num = spoiler + content self.toot_fields.count.set_text(str(self.toot_max_len - num)) def handle_cookie_domains(self, data_manager, result): data = data_manager.fetch_finish(result) print(set(item.get_name() for item in data)) def handle_status_button(self, name): try: if not self[name].get_active(): return except AttributeError: pass tab = self.tab if name == 'debug': #self.window.notification('Merp!', 'INFO', timeout=0, system=True) print(asyncio.get_running_loop()) #if self.window.themes.current: #self.window.themes.unset() #else: #self.window.themes.set('test') elif name == 'bookmark': if not tab.url: return self['bookmark-popover'].grab_focus() with self.db.session as s: self.bookmark_row = s.get_bookmark(tab.url) if not self.bookmark_row: self['bookmark-delete'].set_sensitive(False) return self.bookmark_set_data( name = tab.title, url = tab.url, category = 'Default' ) self['bookmark-delete'].set_sensitive(True) self.bookmark_set_data( name = self.bookmark_row.name, url = self.bookmark_row.url, category = self.bookmark_row.category, description = self.bookmark_row.description ) elif name == 'siteoptions': if not self['siteoptions'].get_active() or not tab.url.hostname(): return with self.db.session as s: row = s.get_permission(tab.url.hostname()) self['siteoptions-domain'].set_text(tab.url.hostname()) self['siteoptions-reset'].set_sensitive(True if row != None else False) self.siteoptions_row = row with self.block_signals(*[f'siteoptions-{name}' for name in SITEOPTIONS]): for k, v in default_permissions.items(): value = row.get(k, v) self[f'siteoptions-{k}'].set_state(value) self['siteoptions-reset'].set_sensitive(row and row.id != None) self['siteoptions-reset'].grab_focus() elif name == 'logins': self['logins-saved-list'].grab_focus() self.handle_logins_refresh() elif name == 'toot': if not self.toot_set_account(): return self['toot-popover'].popdown() self.toot_max_len = self.toot_account.toot_limit self.toot_update_count() self.toot_set_reply(self.toot_post) self['toot-content'].grab_focus() def handle_bookmark_button(self, name): with self.db.session as s: if name == 'save': data = self.bookmark_get_data() if self.bookmark_row: s.put_bookmark_row(self.bookmark_row, **data) else: s.put_bookmark(**data) elif name == 'delete': if self.bookmark_row: s.remove_row(self.bookmark_row) self['bookmark-popover'].popdown() def handle_bookmark_category_button(self): menu = self['bookmark-category-menu'] menu.popup_at_widget( widget = self['bookmark-category'], widget_anchor = Gdk.Gravity.NORTH_EAST, menu_anchor = Gdk.Gravity.SOUTH_EAST, trigger_event = None ) def handle_bookmark_set_category(self, category): self['bookmark-category'].set_text(category) def handle_fedi_button(self, name): post = self.tab.fedi_post if not post or not self.toot_set_account(): return if name == 'reply': #self.toot_set_reply(post) self.toot_post = post self['toot'].activate() elif name == 'favorite': if post.favourited == True: if self.toot_account.api.status_unfavourite(post.id): post.favourited = False self.window.notification('Unfavorited post') else: self.window.notification('Failed to unfavorite post', 'error') else: if self.toot_account.api.status_favourite(post.id): post.favourited = True self.window.notification('Favorited post') else: self.window.notification('Failed to favorite post', 'error') elif name == 'boost': if post.reblogged == True: if self.toot_account.api.status_unreblog(post.id): post.reblogged = False self.window.notification('Unboosted post') else: self.window.notification('Failed to unboost post', 'error') else: if self.toot_account.api.status_reblog(post.id): post.reblogged = True self.window.notification('Boosted post') else: self.window.notification('Failed to boost post', 'error') if post: cache.posts.store(self.tab.url, post) def handle_siteoptions_switch(self, name): active = self[f'siteoptions-{name}'].get_active() try: host = self.tab.url.hostname() except: return with self.db.session as s: s.put_permission(host, name, active) logging.verbose(f'siteoptions: set {name}: {active}') self['siteoptions-reset'].set_sensitive(True) def handle_siteoptions_button(self, name): if name == 'reset': host = self.tab.url.hostname() with self.db.session as s: s.del_permission(host) self.handle_status_button('siteoptions') elif name == 'close': self['siteoptions-popover'].popdown() def handle_logins_button(self, name): if name == 'clear': for widget in self['logins-unsaved-list']: widget.handle_cancel() elif name == 'passgen': self.app.clipboard.set_text(random_str(extra=r'!@#$%^&*()_+-={}[]<>;:'), -1) self.window.notification('Copied newly-generated password to clipboard', system=False) self['logins-popover'].popdown() def handle_logins_refresh(self): login_list = self['logins-saved-list'] url = self.tab.url unsaved = len(self['logins-unsaved-list']) for child in login_list.get_children(): child.destroy() for row in self.app.password.fetch(domain=url.hostname()): ## todo: fix searching by domain if row.domain == url.hostname(): login_list.add(SavedLoginRow(row, url)['container']) for name in ['scroll', 'label', 'clear']: widget = self[f'logins-unsaved-{name}'] if unsaved: widget.show() else: widget.hide() def handle_toot_key_press(self, textview, event, *args): keyname = Gdk.keyval_name(event.keyval) if keyname.lower() == 'return' and event.state == Gdk.ModifierType.CONTROL_MASK: self.toot_send() return True def handle_toot_spoiler_activate(self, *args): self['toot-content'].grab_focus() def setup(self): self.bookmark_fields = DotDict( name = self['bookmark-title'], url = self['bookmark-url'], category = self['bookmark-category'], description = self['bookmark-description'] ) self.toot_fields = DotDict( reply = self['toot-reply'], spoiler = self['toot-spoiler'], content = self['toot-content'], visibility = self['toot-visibility'], count = self['toot-count'] ) self.logins = DotDict( unsaved = self['logins-unsaved-list'], saved = self['logins-saved-list'] ) ## Status bar self.connect('debug', 'clicked', self.handle_status_button, 'debug') self.connect('toot', 'toggled', self.handle_status_button, 'toot') self.connect('reply', 'clicked', self.handle_fedi_button, 'reply') self.connect('boost', 'clicked', self.handle_fedi_button, 'boost') self.connect('favorite', 'clicked', self.handle_fedi_button, 'favorite') self.connect('logins', 'toggled', self.handle_status_button, 'logins') self.connect('bookmark', 'toggled', self.handle_status_button, 'bookmark') self.connect('siteoptions', 'toggled', self.handle_status_button, 'siteoptions') ## Toot self.connect('toot-spoiler', 'changed', self.toot_update_count) self.connect('toot-spoiler', 'activate', self.handle_toot_spoiler_activate) self.connect('toot-reset', 'clicked', self.toot_clear) self.connect('toot-send', 'clicked', self.toot_send) self.connect('toot-close', 'clicked', self.toot_close) self.connect('toot-content', 'key-press-event', self.handle_toot_key_press) connect(self['toot-content'].get_buffer(), 'changed', self.toot_update_count) ## Logins self.connect('logins-close', 'clicked', self.handle_logins_button, 'close') self.connect('logins-unsaved-clear', 'clicked', self.handle_logins_button, 'clear') self.connect('logins-saved-passgen', 'clicked', self.handle_logins_button, 'passgen') ## Bookmarks self.connect('bookmark-save', 'clicked', self.handle_bookmark_button, 'save') self.connect('bookmark-delete', 'clicked', self.handle_bookmark_button, 'delete') self.connect('bookmark-close', 'clicked', self.handle_bookmark_button, 'close') self.connect('bookmark-category', 'icon-press', self.handle_bookmark_category_button) ## Siteoptions self.connect('siteoptions-reset', 'clicked', self.handle_siteoptions_button, 'reset') self.connect('siteoptions-close', 'clicked', self.handle_siteoptions_button, 'close') # extra work needs to be done to toggle webview settings self.permissions = DotDict({ 'instance': False, 'adblock': True, 'fullscreen': True, 'autoplay': False, 'dialog': True, 'notification': False, 'microphone': False, 'location': False, 'camera': False, #'javascript': True, #'images': True, #'autoplay': False, #'insecure': False }) for name in self.permissions: self.connect(f'siteoptions-{name}', 'state-set', self.handle_siteoptions_switch, name) self.bookmark_refresh_categories()