Update user avatar on login

This commit is contained in:
bleakgrey 2018-04-14 20:18:42 +03:00
parent 30f8348286
commit ba20c1a287
14 changed files with 391 additions and 12 deletions

View File

@ -1,13 +1,27 @@
public class Tootle.Account{ public class Tootle.Account{
public int id = -1; public int64 id;
public string username; public string username;
public string username_acct; public string acct;
public string display_name; public string display_name;
public string note; public string note;
public string avatar;
public Account(){ public Account(int64 id){
this.id = id;
}
public static Account parse(Json.Object obj) {
var id = int64.parse (obj.get_string_member ("id"));
var account = new Account (id);
account.username = obj.get_string_member ("username");
account.acct = obj.get_string_member ("acct");
account.display_name = obj.get_string_member ("display_name");
account.note = obj.get_string_member ("note");
account.avatar = obj.get_string_member ("avatar");
return account;
} }
} }

13
src/API/Account.vala~ Normal file
View File

@ -0,0 +1,13 @@
public class Tootle.Account{
public int id = -1;
public string username;
public string username_acct;
public string display_name;
public string note;
public Account(){
}
}

View File

@ -2,6 +2,11 @@ using GLib;
public class Tootle.AccountManager : Object{ public class Tootle.AccountManager : Object{
public abstract signal void changed_current(Account account);
public abstract signal void added(Account account);
public abstract signal void removed(Account account);
private static Account current;
private static Settings settings; private static Settings settings;
private static AccountManager _instance; private static AccountManager _instance;
public static AccountManager instance{ public static AccountManager instance{
@ -93,5 +98,21 @@ public class Tootle.AccountManager : Object{
}); });
return msg; return msg;
} }
public Soup.Message update_current (){
var msg = new Soup.Message("GET", settings.instance_url + "/api/v1/accounts/verify_credentials");
NetManager.instance.queue(msg, (sess, mess) => {
try{
var root = NetManager.parse (mess);
current = Account.parse(root);
changed_current (current);
}
catch (GLib.Error e) {
warning ("Can't get current user");
warning (e.message);
}
});
return msg;
}
} }

97
src/AccountManager.vala~ Normal file
View File

@ -0,0 +1,97 @@
using GLib;
public class Tootle.AccountManager : Object{
private static Settings settings;
private static AccountManager _instance;
public static AccountManager instance{
get{
if(_instance == null)
_instance = new AccountManager();
return _instance;
}
}
construct{
settings = Settings.instance;
}
public AccountManager(){
Object();
}
public bool has_client_tokens(){
var client_id = settings.client_id;
var client_secret = settings.client_secret;
return !(client_id == "null" || client_secret == "null");
}
public bool has_access_token (){
return settings.access_token != "null";
}
public void request_auth_code (string client_id){
var pars = "?scope=read%20write%20follow";
pars += "&response_type=code";
pars += "&redirect_uri=urn:ietf:wg:oauth:2.0:oob";
pars += "&client_id=" +client_id;
try {
AppInfo.launch_default_for_uri (settings.instance_url + "/oauth/authorize" + pars, null);
}
catch (GLib.Error e){
warning (e.message);
}
}
public Soup.Message request_client_tokens(){
var pars = "?client_name=Tootle";
pars += "&redirect_uris=urn:ietf:wg:oauth:2.0:oob";
pars += "&scopes=read%20write%20follow";
var msg = new Soup.Message("POST", settings.instance_url + "/api/v1/apps" + pars);
NetManager.instance.queue(msg, (sess, mess) => {
try{
var root = NetManager.parse (mess);
var client_id = root.get_string_member ("client_id");
var client_secret = root.get_string_member ("client_secret");
settings.client_id = client_id;
settings.client_secret = client_secret;
debug ("Received tokens");
request_auth_code (client_id);
}
catch (GLib.Error e) {
warning ("Can't request client secret.");
warning (e.message);
}
});
return msg;
}
public Soup.Message try_auth (string code){
var pars = "?client_id=" + settings.client_id;
pars += "&client_secret=" + settings.client_secret;
pars += "&redirect_uri=urn:ietf:wg:oauth:2.0:oob";
pars += "&grant_type=authorization_code";
pars += "&code=" + code;
var msg = new Soup.Message("POST", settings.instance_url + "/oauth/token" + pars);
NetManager.instance.queue(msg, (sess, mess) => {
try{
var root = NetManager.parse (mess);
var access_token = root.get_string_member ("access_token");
settings.access_token = access_token;
debug ("Got access token");
Tootle.app.state_updated ();
}
catch (GLib.Error e) {
warning ("Can't get access token");
warning (e.message);
}
});
return msg;
}
}

View File

@ -22,12 +22,13 @@ public class Tootle.CacheManager : GLib.Object{
Object (); Object ();
} }
public void load_image (string url, Granite.Widgets.Avatar avatar){ //TODO: actually cache images
public void load_avatar (string url, Granite.Widgets.Avatar avatar, int size = 32){
var msg = new Soup.Message("GET", url); var msg = new Soup.Message("GET", url);
msg.finished.connect(()=>{ msg.finished.connect(()=>{
uint8[] buf = msg.response_body.data; uint8[] buf = msg.response_body.data;
var loader = new PixbufLoader(); var loader = new PixbufLoader();
loader.set_size (32,32); loader.set_size (size, size);
loader.write(buf); loader.write(buf);
loader.close(); loader.close();
var pixbuf = loader.get_pixbuf (); var pixbuf = loader.get_pixbuf ();

41
src/CacheManager.vala~ Normal file
View File

@ -0,0 +1,41 @@
using Gdk;
using GLib;
public class Tootle.CacheManager : GLib.Object{
private static CacheManager _instance;
public static CacheManager instance{
get{
if(_instance == null)
_instance = new CacheManager();
return _instance;
}
}
private static string path_images;
construct{
path_images = GLib.Environment.get_user_special_dir (UserDirectory.DOWNLOAD);
}
public CacheManager(){
Object ();
}
//TODO: actually cache images
public void load_image (string url, Granite.Widgets.Avatar avatar){
var msg = new Soup.Message("GET", url);
msg.finished.connect(()=>{
uint8[] buf = msg.response_body.data;
var loader = new PixbufLoader();
loader.set_size (32,32);
loader.write(buf);
loader.close();
var pixbuf = loader.get_pixbuf ();
avatar.pixbuf = pixbuf;
});
NetManager.instance.queue(msg, (sess, mess) => {});
}
}

View File

@ -83,6 +83,8 @@ public class Tootle.MainWindow: Gtk.Window {
show_setup_views (); show_setup_views ();
else else
show_main_views (); show_main_views ();
AccountManager.instance.update_current ();
} }
private void show_setup_views (){ private void show_setup_views (){

View File

@ -46,7 +46,7 @@ public class Tootle.NetManager : GLib.Object{
public static Json.Object parse(Soup.Message msg) throws GLib.Error{ public static Json.Object parse(Soup.Message msg) throws GLib.Error{
// stdout.printf ("Status Code: %u\n", msg.status_code); // stdout.printf ("Status Code: %u\n", msg.status_code);
// stdout.printf ("Message length: %lld\n", msg.response_body.length); // stdout.printf ("Message length: %lld\n", msg.response_body.length);
// stdout.printf ("Data: \n%s\n", (string) msg.response_body.data); // stdout.printf ("Data: \n%s\n", (string) msg.response_body.data);
var parser = new Json.Parser (); var parser = new Json.Parser ();

67
src/NetManager.vala~ Normal file
View File

@ -0,0 +1,67 @@
using Soup;
using GLib;
using Json;
public class Tootle.NetManager : GLib.Object{
public abstract signal void started();
public abstract signal void finished();
private static NetManager _instance;
public static NetManager instance{
get{
if(_instance == null)
_instance = new NetManager();
return _instance;
}
}
private int requests_processing = 0;
private Soup.Session session;
construct{
session = new Soup.Session ();
session.request_unqueued.connect (() => {
requests_processing--;
if(requests_processing <= 0)
finished ();
});
}
public NetManager(){
GLib.Object();
}
public Soup.Message queue(Soup.Message msg, Soup.SessionCallback cb){
requests_processing++;
started ();
var token = Settings.instance.access_token;
if(token != "null")
msg.request_headers.append ("Authorization", "Bearer " + token);
session.queue_message (msg, cb);
return msg;
}
public static Json.Object parse(Soup.Message msg) throws GLib.Error{
stdout.printf ("Status Code: %u\n", msg.status_code);
stdout.printf ("Message length: %lld\n", msg.response_body.length);
stdout.printf ("Data: \n%s\n", (string) msg.response_body.data);
var parser = new Json.Parser ();
parser.load_from_data ((string) msg.response_body.flatten ().data, -1);
return parser.get_root ().get_object ();
}
public static Json.Array parse_array(Soup.Message msg) throws GLib.Error{
// stdout.printf ("Status Code: %u\n", msg.status_code);
// stdout.printf ("Message length: %lld\n", msg.response_body.length);
// stdout.printf ("Data: \n%s\n", (string) msg.response_body.data);
var parser = new Json.Parser ();
parser.load_from_data ((string) msg.response_body.flatten ().data, -1);
return parser.get_root ().get_array ();
}
}

View File

@ -26,9 +26,9 @@ public class Tootle.HomeView : Tootle.AbstractView {
base (true); base (true);
this.timeline = timeline; this.timeline = timeline;
this.pars = pars; this.pars = pars;
request_update ();
show_all(); show_all();
request_update ();
// var s = new Status(1); // var s = new Status(1);
// s.content = "Test content, wow!"; // s.content = "Test content, wow!";

77
src/Views/HomeView.vala~ Normal file
View File

@ -0,0 +1,77 @@
using Gtk;
using Gdk;
public class Tootle.HomeView : Tootle.AbstractView {
Gtk.Box view;
Gtk.ScrolledWindow scroll;
private string timeline;
private string pars;
construct {
view = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
view.hexpand = true;
view.valign = Gtk.Align.START;
scroll = new Gtk.ScrolledWindow (null, null);
scroll.hexpand = true;
scroll.vexpand = true;
scroll.hscrollbar_policy = Gtk.PolicyType.NEVER;
scroll.add (view);
add (scroll);
}
public HomeView (string timeline = "home", string pars = "") {
base (true);
this.timeline = timeline;
this.pars = pars;
request_update ();
show_all();
// var s = new Status(1);
// s.content = "Test content, wow!";
// prepend (s);
}
public override string get_icon () {
return "user-home-symbolic";
}
public override string get_name () {
return "Home Timeline";
}
public void prepend(Status status){
var widget = new StatusWidget();
widget.rebind (status);
view.pack_end (widget, false, false, 0);
}
public virtual Soup.Message request_update (){
var url = Settings.instance.instance_url;
url += "api/v1/timelines/";
url += this.timeline;
url += this.pars;
var msg = new Soup.Message("GET", url);
NetManager.instance.queue(msg, (sess, mess) => {
try{
NetManager.parse_array (mess).foreach_element ((array, i, node) => {
var object = node.get_object ();
if (object != null){
var status = Status.parse(object);
prepend (status);
}
});
}
catch (GLib.Error e) {
warning ("Can't update feed");
warning (e.message);
}
});
return msg;
}
}

View File

@ -7,9 +7,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
Gtk.Popover menu; Gtk.Popover menu;
construct{ construct{
//var iconfile = "/var/lib/AccountsService/icons/blue";
avatar = new Granite.Widgets.Avatar.with_default_icon (24); avatar = new Granite.Widgets.Avatar.with_default_icon (24);
avatar.set_tooltip_text (_("Account Options"));
avatar.button_press_event.connect(event => { avatar.button_press_event.connect(event => {
return false; return false;
}); });
@ -35,6 +33,10 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
popover = menu; popover = menu;
add(avatar); add(avatar);
show_all (); show_all ();
AccountManager.instance.changed_current.connect (account => {
CacheManager.instance.load_avatar (account.avatar, avatar, 24);
});
} }
public AccountsButton(){ public AccountsButton(){

View File

@ -0,0 +1,43 @@
using Gtk;
public class Tootle.AccountsButton : Gtk.MenuButton{
Granite.Widgets.Avatar avatar;
Gtk.Grid grid;
Gtk.Popover menu;
construct{
avatar = new Granite.Widgets.Avatar.with_default_icon (24);
avatar.set_tooltip_text (_("Account Options"));
avatar.button_press_event.connect(event => {
return false;
});
var item_separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
item_separator.hexpand = true;
item_separator.margin_top = 6;
var item_settings = new Gtk.ModelButton ();
item_settings.text = _("Settings");
grid = new Gtk.Grid ();
grid.orientation = Gtk.Orientation.VERTICAL;
grid.width_request = 200;
grid.attach(item_separator, 0, 1, 1, 1);
grid.attach(item_settings, 0, 2, 1, 1);
grid.show_all ();
menu = new Gtk.Popover (null);
menu.add (grid);
get_style_context ().add_class ("button_avatar");
popover = menu;
add(avatar);
show_all ();
}
public AccountsButton(){
Object();
}
}

View File

@ -13,6 +13,7 @@ public class Tootle.StatusWidget : Gtk.Grid {
construct { construct {
margin = 6; margin = 6;
avatar = new Granite.Widgets.Avatar.with_default_icon (32); avatar = new Granite.Widgets.Avatar.with_default_icon (32);
avatar.valign = Gtk.Align.START; avatar.valign = Gtk.Align.START;
avatar.margin_end = 6; avatar.margin_end = 6;
@ -32,7 +33,7 @@ public class Tootle.StatusWidget : Gtk.Grid {
reblogs = new Gtk.Label ("0"); reblogs = new Gtk.Label ("0");
favorites = new Gtk.Label ("0"); favorites = new Gtk.Label ("0");
counters = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); counters = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); //TODO: currently useless
counters.margin_top = 6; counters.margin_top = 6;
counters.margin_bottom = 12; counters.margin_bottom = 12;
counters.add(new Gtk.Image.from_icon_name ("media-playlist-repeat-symbolic", Gtk.IconSize.SMALL_TOOLBAR)); counters.add(new Gtk.Image.from_icon_name ("media-playlist-repeat-symbolic", Gtk.IconSize.SMALL_TOOLBAR));
@ -45,7 +46,7 @@ public class Tootle.StatusWidget : Gtk.Grid {
attach(user, 1, 1, 1, 1); attach(user, 1, 1, 1, 1);
attach(content, 1, 2, 1, 1); attach(content, 1, 2, 1, 1);
attach(counters, 1, 3, 1, 1); attach(counters, 1, 3, 1, 1);
show_all(); show_all(); //TODO: display conversations
} }
public StatusWidget () { public StatusWidget () {
@ -59,7 +60,7 @@ public class Tootle.StatusWidget : Gtk.Grid {
reblogs.label = status.reblogs_count.to_string (); reblogs.label = status.reblogs_count.to_string ();
favorites.label = status.favourites_count.to_string (); favorites.label = status.favourites_count.to_string ();
CacheManager.instance.load_image (status.avatar, this.avatar); CacheManager.instance.load_avatar (status.avatar, this.avatar);
} }
} }