Commit 4a66045e authored by Daniel Brötzmann's avatar Daniel Brötzmann

Profile: Add avatar and nickname

parent 02646c34
Pipeline #6742 failed with stages
in 2 minutes and 53 seconds
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.1 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkPopover" id="privacy_popover">
<property name="can-focus">False</property>
<child>
<!-- n-columns=2 n-rows=4 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Privacy</property>
<style>
<class name="bold"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Contact Infos</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Picture and Name</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-bottom">6</property>
<property name="label" translatable="yes">Make your profile visible for everyone (on) or just for your contacts (off).</property>
<property name="wrap">True</property>
<property name="max-width-chars">42</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="avatar_nick_access">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="halign">start</property>
<property name="active">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="vcard_access">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="halign">start</property>
<property name="active">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<style>
<class name="padding-6"/>
</style>
</object>
</child>
</object>
<object class="GtkStack" id="profile_stack">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="transition-type">crossfade</property>
<child>
<object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="active">True</property>
</object>
<packing>
<property name="name">spinner</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="profile_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkOverlay" id="avatar_overlay">
<property name="width-request">100</property>
<property name="height-request">100</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<child>
<object class="GtkImage" id="avatar_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">avatar-default-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="index">1</property>
</packing>
</child>
<child type="overlay">
<object class="GtkButton" id="remove_avatar_button">
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="tooltip-text" translatable="yes">Remove your profile picture</property>
<property name="halign">start</property>
<property name="valign">end</property>
<property name="always-show-image">True</property>
<signal name="clicked" handler="_on_remove_avatar" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">user-trash-symbolic</property>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkButton" id="edit_avatar_button">
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="tooltip-text" translatable="yes">Change your profile picture</property>
<property name="halign">end</property>
<property name="valign">end</property>
<property name="always-show-image">True</property>
<signal name="clicked" handler="_on_edit_avatar" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">document-edit-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="index">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="nickname_entry">
<property name="name">NicknameEntry</property>
<property name="width-request">250</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Enter your nickname. This is how your name is displayed to your contacts.</property>
<property name="halign">center</property>
<property name="xalign">0.5</property>
<property name="placeholder-text" translatable="yes">Name</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkActionBar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="use-underline">True</property>
<signal name="clicked" handler="_on_cancel_clicked" swapped="no"/>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child type="center">
<object class="GtkMenuButton" id="add_entry_button">
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="use-popover">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">list-add-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Add Entry</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="privacy_button">
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="tooltip-text" translatable="yes">Privacy</property>
<property name="direction">up</property>
<property name="popover">privacy_popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">preferences-system-privacy-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="label" translatable="yes">_Save</property>
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="no-show-all">True</property>
<property name="use-underline">True</property>
<signal name="clicked" handler="_on_save_clicked" swapped="no"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="edit_button">
<property name="label" translatable="yes">_Edit</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<signal name="clicked" handler="_on_edit_clicked" swapped="no"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">profile</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="avatar_selector_box">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="spacing">12</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<signal name="clicked" handler="_on_cancel_update_avatar" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="avatar_update_button">
<property name="label" translatable="yes">Update</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="clicked" handler="_on_update_avatar" swapped="no"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="name">avatar_selector</property>
<property name="position">2</property>
</packing>
</child>
<style>
<class name="padding-18"/>
</style>
</object>
</interface>
......@@ -35,6 +35,15 @@
/* VCardWindow */
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
#NicknameEntry:disabled {
font-size: 28px;
font-weight: bold;
border: none;
padding: 0px 0px 0px 0px;
background-color: @theme_unfocused_bg_color;
color: @theme_fg_color;
}
#ProfileWindow grid { padding: 18px; }
#ProfileWindow grid > button {
padding-left: 4px;
......
......@@ -3,14 +3,21 @@
from gi.repository import Gio
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from nbxmpp.errors import StanzaError
# from nbxmpp.modules.vcard_temp import VCard
from nbxmpp.modules.vcard4 import VCard
from gajim.common import app
from gajim.common.const import AvatarSize
from gajim.common.i18n import _
from gajim.common.i18n import Q_
from gajim.gtk.avatar import clip_circle
from gajim.gtk.avatar_selector import AvatarSelector
from gajim.gtk.filechoosers import AvatarChooserDialog
from gajim.gtk.util import get_builder
from gajim.gtk.vcard_grid import VCardGrid
log = logging.getLogger('gajim.gtk.profile')
......@@ -38,21 +45,14 @@ def __init__(self, account, *args):
self.set_show_menubar(False)
self.set_resizable(True)
self.set_default_size(600, 600)
self.set_default_size(700, 600)
self.set_name('ProfileWindow')
self.set_title(_('Profile'))
self.account = account
self._jid = app.get_jid_from_account(account)
self._stack = Gtk.Stack()
self._stack.get_style_context().add_class('padding-18')
self._spinner = Gtk.Spinner()
self._spinner.start()
self._stack.add_named(self._spinner, 'spinner')
self._edit_button = Gtk.Button.new_with_mnemonic(label=_('_Edit'))
self._edit_button.get_style_context().add_class('suggested-action')
self._edit_button.connect('clicked', self._on_edit_clicked)
self._ui = get_builder('profile_new.ui')
# 'Add entry' menu for edit mode
menu = Gio.Menu()
......@@ -61,72 +61,56 @@ def __init__(self, account, *args):
continue
menu.append(label, 'win.add-' + action.lower())
self._add_entry_button = Gtk.MenuButton()
menubox = Gtk.Box(spacing=6)
menubox.add(Gtk.Label(label=_('Add Entry')))
menubox.add(Gtk.Image.new_from_icon_name(
'list-add-symbolic', Gtk.IconSize.MENU))
self._add_entry_button.add(menubox)
self._add_entry_button.set_menu_model(menu)
self._add_entry_button.set_no_show_all(True)
self._ui.add_entry_button.set_menu_model(menu)
self._add_actions()
self._cancel_button = Gtk.Button.new_with_mnemonic(label=_('_Cancel'))
self._cancel_button.connect('clicked', self._on_cancel_clicked)
self._cancel_button.set_no_show_all(True)
self._save_button = Gtk.Button.new_with_mnemonic(label=_('_Save'))
self._save_button.get_style_context().add_class('suggested-action')
self._save_button.connect('clicked', self._on_save_clicked)
self._save_button.set_no_show_all(True)
self._avatar_selector = None
self._current_avatar = None
# Actionbar for edit mode
actionbar = Gtk.ActionBar()
actionbar.pack_end(self._edit_button)
actionbar.set_center_widget(self._add_entry_button)
actionbar.pack_end(self._save_button)
actionbar.pack_end(self._cancel_button)
self._ui.nickname_entry.set_text(app.nicks[account])
profile_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
scrolled = Gtk.ScrolledWindow()
self._profile = VCardGrid(self.account)
scrolled.add(self._profile)
profile_box.pack_start(scrolled, True, True, 0)
profile_box.pack_end(actionbar, False, False, 0)
self._stack.add_named(profile_box, 'profile')
self._ui.profile_box.add(self._profile)
self.add(self._stack)
self.add(self._ui.profile_stack)
self.show_all()
client = app.get_client(account)
client._client.get_module('VCard4').request_vcard(
callback=self._on_vcard_received)
self._load_avatar()
self._ui.connect_signals(self)
self.connect('key-press-event', self._on_key_press_event)
def _on_vcard_received(self, task):
try:
vcard = task.finish()
except StanzaError as error:
# TODO Handle error
return
log.info('Error loading VCard: %s', error)
vcard = VCard()
self._load_avatar()
self._profile.set_vcard(vcard)
self._stack.set_visible_child_name('profile')
self._spinner.stop()
self._ui.profile_stack.set_visible_child_name('profile')
self._ui.spinner.stop()
def _load_avatar(self):
scale = self.get_scale_factor()
self._current_avatar = app.contacts.get_avatar(
self.account,
self._jid,
AvatarSize.VCARD,
scale)
self._ui.avatar_image.set_from_surface(self._current_avatar)
self._ui.avatar_image.show()
def _on_key_press_event(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
def _on_edit_clicked(self, *args):
self._profile.set_editable(True)
self._edit_button.hide()
self._add_entry_button.set_no_show_all(False)
self._add_entry_button.show_all()
self._cancel_button.show()
self._save_button.show()
def _add_actions(self):
for action in MENU_DICT:
if action in ('PHOTO', 'NICKNAME'):
......@@ -141,40 +125,114 @@ def _on_action(self, action, _param):
key = name.split('-')[1]
self._profile.add_new_property(key)
def _on_edit_clicked(self, *args):
self._profile.set_editable(True)
self._ui.edit_button.hide()
self._ui.add_entry_button.set_no_show_all(False)
self._ui.add_entry_button.show_all()
self._ui.cancel_button.show()
self._ui.save_button.show()
self._ui.remove_avatar_button.show()
self._ui.edit_avatar_button.show()
self._ui.nickname_entry.set_sensitive(True)
self._ui.privacy_button.show()
def _on_cancel_clicked(self, _widget):
self._profile.set_editable(False)
self._edit_button.show()
self._add_entry_button.hide()
self._cancel_button.hide()
self._save_button.hide()
self._ui.edit_button.show()
self._ui.add_entry_button.hide()
self._ui.cancel_button.hide()
self._ui.save_button.hide()
self._ui.remove_avatar_button.hide()
self._ui.edit_avatar_button.hide()
self._ui.privacy_button.hide()
self._ui.nickname_entry.set_sensitive(False)
self._ui.avatar_image.set_from_surface(self._current_avatar)
self._ui.nickname_entry.set_text(app.nicks[self.account])
def _on_save_clicked(self, _widget):
self._spinner.start()
self._stack.set_visible_child_name('spinner')
self._add_entry_button.hide()
self._cancel_button.hide()
self._save_button.hide()
self._edit_button.show()
self._ui.spinner.start()
self._ui.profile_stack.set_visible_child_name('spinner')
self._ui.add_entry_button.hide()
self._ui.cancel_button.hide()
self._ui.save_button.hide()
self._ui.edit_button.show()
self._ui.remove_avatar_button.hide()
self._ui.edit_avatar_button.hide()
self._ui.privacy_button.hide()
self._ui.nickname_entry.set_sensitive(False)
# iq = self._profile.get_vcard_data()
# public = self._ui.vcard_access.get_active()
con = app.connections[self.account]
con._client.get_module('VCard4').set_vcard(self._profile.get_vcard(),