Update user avatar on login
This commit is contained in:
parent
30f8348286
commit
ba20c1a287
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 ();
|
||||||
|
|
|
@ -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) => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 (){
|
||||||
|
|
|
@ -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 ();
|
||||||
|
|
|
@ -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 ();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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!";
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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(){
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue