From d7ff13311c80120ae5074be855d37cc6c974bfc4 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Sun, 18 Jun 2023 08:52:44 +0300 Subject: [PATCH 01/14] feat: initial mk support --- src/API/Account.vala | 5 + src/API/Misskey.vala | 139 +++++++++++++++++++++ src/API/meson.build | 1 + src/Dialogs/NewAccount.vala | 57 ++++++--- src/Services/Accounts/AccountStore.vala | 24 ++-- src/Services/Accounts/InstanceAccount.vala | 7 +- src/Services/Network/Request.vala | 8 +- 7 files changed, 211 insertions(+), 30 deletions(-) create mode 100644 src/API/Misskey.vala diff --git a/src/API/Account.vala b/src/API/Account.vala index 977dc418a..f7ffb1f21 100644 --- a/src/API/Account.vala +++ b/src/API/Account.vala @@ -118,4 +118,9 @@ public class Tuba.API.Account : Entity, Widgetizable { } } + public Account.empty (string t_id = "") { + Object ( + id: t_id + ); + } } diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala new file mode 100644 index 000000000..6c24c9f35 --- /dev/null +++ b/src/API/Misskey.vala @@ -0,0 +1,139 @@ +public class Tuba.API.Misskey : Object { + public class JSON : Object { + public static string get_app (string callback_url) { + var builder = new Json.Builder (); + builder.begin_object (); + builder.set_member_name ("name"); + builder.add_string_value (Build.NAME); + + builder.set_member_name ("description"); + builder.add_string_value ("Browse the Fediverse"); + + builder.set_member_name ("permission"); + builder.begin_array (); + builder.add_string_value ("write:user-groups"); + builder.add_string_value ("read:user-groups"); + builder.add_string_value ("read:page-likes"); + builder.add_string_value ("write:page-likes"); + builder.add_string_value ("write:pages"); + builder.add_string_value ("read:pages"); + builder.add_string_value ("write:votes"); + builder.add_string_value ("write:reactions"); + builder.add_string_value ("read:reactions"); + builder.add_string_value ("write:notifications"); + builder.add_string_value ("read:notifications"); + builder.add_string_value ("write:notes"); + builder.add_string_value ("write:mutes"); + builder.add_string_value ("read:mutes"); + builder.add_string_value ("read:account"); + builder.add_string_value ("write:account"); + builder.add_string_value ("read:blocks"); + builder.add_string_value ("write:blocks"); + builder.add_string_value ("read:drive"); + builder.add_string_value ("write:drive"); + builder.add_string_value ("read:favorites"); + builder.add_string_value ("write:favorites"); + builder.add_string_value ("read:following"); + builder.add_string_value ("write:following"); + builder.add_string_value ("read:messaging"); + builder.add_string_value ("write:messaging"); + builder.end_array (); + + builder.set_member_name ("callbackUrl"); + builder.add_string_value (callback_url); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_session_generate (string secret) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("appSecret"); + builder.add_string_value (secret); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_session_userkey (string secret, string token) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("appSecret"); + builder.add_string_value (secret); + + builder.set_member_name ("token"); + builder.add_string_value (token); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_show_userid (string userid) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("userId"); + builder.add_string_value (userid); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + } + + public static string generate_i (string secret, string access_token) { + string pre_c = @"$access_token$secret"; + Checksum checksum = new Checksum (ChecksumType.SHA256); + + checksum.update (pre_c.data, -1); + return checksum.get_string (); + } + + public static API.Account to_mastodon_account (Json.Node node) { + var root = node.get_object (); + + var mk_name = root.get_string_member ("name"); + var mk_username = root.get_string_member ("username"); + var mk_host = root.get_string_member ("host"); + var mk_avatar_url = root.get_string_member ("avatarUrl"); + var mk_description = root.get_string_member ("description"); + var mk_is_locked = root.get_boolean_member ("isLocked"); + var mk_url = root.get_string_member ("url"); + var mk_created_at = root.get_string_member ("createdAt"); + var mk_banner_url = root.get_string_member ("bannerUrl"); + var mk_followers_count = root.get_int_member ("followersCount"); + var mk_following_count = root.get_int_member ("followingCount"); + var mk_notes_count = root.get_int_member ("notesCount"); + // fields + // pinned + + var masto_acc = new API.Account.empty (root.get_string_member ("id")); + masto_acc.username = mk_username; + masto_acc.acct = mk_host != null ? @"$mk_username@$mk_host" : mk_username; + masto_acc.note = mk_description ?? ""; + masto_acc.locked = mk_is_locked; + masto_acc.header = mk_banner_url; + masto_acc.avatar = mk_avatar_url; + masto_acc.url = mk_url; + masto_acc.followers_count = mk_followers_count; + masto_acc.following_count = mk_following_count; + masto_acc.statuses_count = mk_notes_count; + masto_acc.created_at = mk_created_at; + + return masto_acc; + } +} diff --git a/src/API/meson.build b/src/API/meson.build index 01ba5a732..fb2a10707 100644 --- a/src/API/meson.build +++ b/src/API/meson.build @@ -11,6 +11,7 @@ sources += files( 'Instance.vala', 'List.vala', 'Mention.vala', + 'Misskey.vala', 'Notification.vala', 'PeerTube.vala', 'Pleroma.vala', diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index af8390bd4..d3fd28b57 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -124,22 +124,42 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { async void register_client () throws Error { message ("Registering client"); - var msg = new Request.POST (@"/api/v1/apps") + // var msg = new Request.POST (@"/api/v1/apps") + // .with_account (account) + // .with_form_data ("client_name", Build.NAME) + // .with_form_data ("redirect_uris", redirect_uri = setup_redirect_uri ()) + // .with_form_data ("scopes", scopes) + // .with_form_data ("website", Build.WEBSITE); + + var msg = new Request.POST ("/api/app/create") .with_account (account) - .with_form_data ("client_name", Build.NAME) - .with_form_data ("redirect_uris", redirect_uri = setup_redirect_uri ()) - .with_form_data ("scopes", scopes) - .with_form_data ("website", Build.WEBSITE); + .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_app (setup_redirect_uri ()).data)); yield msg.await (); var parser = Network.get_parser_from_inputstream(msg.response_body); var root = network.parse (parser); - account.client_id = root.get_string_member ("client_id"); - account.client_secret = root.get_string_member ("client_secret"); + // account.client_id = root.get_string_member ("client_id"); + // account.client_secret = root.get_string_member ("client_secret"); + account.client_id = root.get_string_member ("id"); + account.client_secret = root.get_string_member ("secret"); message ("OK: Instance registered client"); deck.visible_child = code_step; - open_confirmation_page (); + // open_confirmation_page (); + + var mk_msg = new Request.POST ("/api/auth/session/generate") + .with_account (account) + .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_session_generate (account.client_secret).data)); + yield mk_msg.await (); + + parser = Network.get_parser_from_inputstream(mk_msg.response_body); + root = network.parse (parser); + var callbackurl = root.get_string_member ("url"); + warning (account.client_id); + warning (account.client_secret); + warning (callbackurl); + Host.open_uri (callbackurl); + // Process.exit (0); } void open_confirmation_page () { @@ -155,18 +175,25 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { throw new Oopsie.USER (_("Please enter a valid authorization code")); message ("Requesting access token"); - var token_req = new Request.POST (@"/oauth/token") + // var token_req = new Request.POST (@"/oauth/token") + // .with_account (account) + // .with_param ("client_id", account.client_id) + // .with_param ("client_secret", account.client_secret) + // .with_param ("redirect_uri", redirect_uri) + // .with_param ("grant_type", "authorization_code") + // .with_param ("code", code_entry.text); + // yield token_req.await (); + + var token_req = new Request.POST ("/api/auth/session/userkey") .with_account (account) - .with_param ("client_id", account.client_id) - .with_param ("client_secret", account.client_secret) - .with_param ("redirect_uri", redirect_uri) - .with_param ("grant_type", "authorization_code") - .with_param ("code", code_entry.text); + .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_session_userkey (account.client_secret, code_entry.text).data)); yield token_req.await (); var parser = Network.get_parser_from_inputstream(token_req.response_body); var root = network.parse (parser); - account.access_token = root.get_string_member ("access_token"); + // account.access_token = root.get_string_member ("access_token"); + account.access_token = root.get_string_member ("accessToken"); + account.i = API.Misskey.generate_i (account.access_token, account.client_secret); if (account.access_token == null) throw new Oopsie.INSTANCE (_("Instance failed to authorize the access token")); diff --git a/src/Services/Accounts/AccountStore.vala b/src/Services/Accounts/AccountStore.vala index 00b576131..9126cc263 100644 --- a/src/Services/Accounts/AccountStore.vala +++ b/src/Services/Accounts/AccountStore.vala @@ -136,18 +136,18 @@ public abstract class Tuba.AccountStore : GLib.Object { public Gee.ArrayList backend_tests = new Gee.ArrayList (); public async void guess_backend (InstanceAccount account) throws GLib.Error { - var req = new Request.GET ("/api/v1/instance") - .with_account (account); - yield req.await (); - - var parser = Network.get_parser_from_inputstream(req.response_body); - var root = network.parse (parser); - - string? backend = null; - backend_tests.foreach (test => { - backend = test.get_backend (root); - return true; - }); + // var req = new Request.GET ("/api/v1/instance") + // .with_account (account); + // yield req.await (); + + // var parser = Network.get_parser_from_inputstream(req.response_body); + // var root = network.parse (parser); + + string? backend = "Mastodon"; + // backend_tests.foreach (test => { + // backend = test.get_backend (root); + // return true; + // }); if (backend == null) throw new Oopsie.INTERNAL ("This instance is unsupported."); diff --git a/src/Services/Accounts/InstanceAccount.vala b/src/Services/Accounts/InstanceAccount.vala index 647cfc3bb..c1aa5b681 100644 --- a/src/Services/Accounts/InstanceAccount.vala +++ b/src/Services/Accounts/InstanceAccount.vala @@ -25,6 +25,7 @@ public class Tuba.InstanceAccount : API.Account, Streamable { public string? client_id { get; set; } public string? client_secret { get; set; } public string? access_token { get; set; } + public string? i { get; set; default=null; } public Error? error { get; set; } //TODO: use this field when server invalidates the auth token public GLib.ListStore known_places = new GLib.ListStore (typeof (Place)); @@ -121,12 +122,14 @@ public class Tuba.InstanceAccount : API.Account, Streamable { } public async void verify_credentials () throws Error { - var req = new Request.GET ("/api/v1/accounts/verify_credentials").with_account (this); + // var req = new Request.GET ("/api/v1/accounts/verify_credentials").with_account (this); + var req = new Request.POST ("/api/show").with_account (this).body ("application/json", new Bytes.take(API.Misskey.JSON.get_show_userid (this.id).data)); yield req.await (); var parser = Network.get_parser_from_inputstream(req.response_body); var node = network.parse_node (parser); - var updated = API.Account.from (node); + // var updated = API.Account.from (node); + var updated = API.Misskey.to_mastodon_account (node); patch (updated); message (@"$handle: profile updated"); diff --git a/src/Services/Network/Request.vala b/src/Services/Network/Request.vala index 605023d10..212de80a9 100644 --- a/src/Services/Network/Request.vala +++ b/src/Services/Network/Request.vala @@ -70,11 +70,17 @@ public class Tuba.Request : GLib.Object { private string? t_content_type = null; private Bytes? t_body_bytes = null; - public void set_request_body_from_bytes (string? content_type, Bytes? bytes) { + public void set_request_body_from_bytes (string? content_type, Bytes? bytes) { t_content_type = content_type; t_body_bytes = bytes; } + public Request body (string? content_type, Bytes? bytes) { + t_content_type = content_type; + t_body_bytes = bytes; + return this; + } + public Request then (owned Network.SuccessCallback cb) { this.cb = (owned) cb; return this; From 412707e72e9f8123b9af6ae3bc97ffc637bca446 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Mon, 19 Jun 2023 15:15:55 +0300 Subject: [PATCH 02/14] feat: fix login, init timeline --- src/API/Misskey.vala | 17 +++++++++++++ src/API/Misskey/Note.vala | 22 ++++++++++++++++ src/API/Misskey/User.vala | 22 ++++++++++++++++ src/API/Misskey/meson.build | 3 +++ src/API/meson.build | 1 + src/Dialogs/NewAccount.vala | 21 +++++++--------- src/Services/Accounts/AccountStore.vala | 2 +- src/Services/Accounts/InstanceAccount.vala | 7 ++++-- src/Views/Federated.vala | 2 +- src/Views/Timeline.vala | 29 +++++++++++++++++++--- 10 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 src/API/Misskey/Note.vala create mode 100644 src/API/Misskey/User.vala create mode 100644 src/API/Misskey/meson.build diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index 6c24c9f35..b15031ac8 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -93,6 +93,23 @@ public class Tuba.API.Misskey : Object { generator.set_root (builder.get_root ()); return generator.to_data (null); } + + public static string get_timeline (int limit = 20) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("i"); + builder.add_string_value (accounts.active.i); + + builder.set_member_name ("limit"); + builder.add_int_value (limit); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } } public static string generate_i (string secret, string access_token) { diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala new file mode 100644 index 000000000..c9d1445cf --- /dev/null +++ b/src/API/Misskey/Note.vala @@ -0,0 +1,22 @@ +public class Tuba.API.Misskey.Note : Entity { + ~Note () { + message ("[OBJ] Destroyed "+uri); + } + + public string id { get; set; } + public string createdAt { get; set; default = "0"; } + public string text { get; set; default = ""; } + public string? cw { get; set; default = null; } + public API.Account user { get; set; } + public string? replyId { get; set; default = null; } + public string visibility { get; set; default = settings.default_post_visibility; } + public Gee.ArrayList? mentions { get; set; default = null; } + public API.Poll? poll { get; set; default = null; } + public Gee.ArrayList? emojis { get; set; } + public Gee.ArrayList? reactions { get; set; default = null; } + public int64 renotesCount { get; set; default = 0; } + public int64 repliesCount { get; set; default = 0; } + public string uri { get; set; } + public string url { get; set; } + public API.Misskey.Note? renote { get; set; default = null; } +} diff --git a/src/API/Misskey/User.vala b/src/API/Misskey/User.vala new file mode 100644 index 000000000..c828c3b7a --- /dev/null +++ b/src/API/Misskey/User.vala @@ -0,0 +1,22 @@ +public class Tuba.API.Misskey.User : Entity { + ~Note () { + message ("[OBJ] Destroyed "+uri); + } + + public string id { get; set; } + public string createdAt { get; set; default = "0"; } + public string text { get; set; default = ""; } + public string? cw { get; set; default = null; } + public API.Account user { get; set; } + public string? replyId { get; set; default = null; } + public string visibility { get; set; default = settings.default_post_visibility; } + public Gee.ArrayList? mentions { get; set; default = null; } + public API.Poll? poll { get; set; default = null; } + public Gee.ArrayList? emojis { get; set; } + public Gee.ArrayList? reactions { get; set; default = null; } + public int64 renotesCount { get; set; default = 0; } + public int64 repliesCount { get; set; default = 0; } + public string uri { get; set; } + public string url { get; set; } + public API.Misskey.Note? renote { get; set; default = null; } +} diff --git a/src/API/Misskey/meson.build b/src/API/Misskey/meson.build new file mode 100644 index 000000000..caf1f5525 --- /dev/null +++ b/src/API/Misskey/meson.build @@ -0,0 +1,3 @@ +sources += files( + 'Note.vala', +) \ No newline at end of file diff --git a/src/API/meson.build b/src/API/meson.build index fb2a10707..72a28e7ab 100644 --- a/src/API/meson.build +++ b/src/API/meson.build @@ -29,5 +29,6 @@ subdir('Account') subdir('BookWyrm') subdir('Funkwhale') subdir('Instance') +subdir('Misskey') subdir('PeerTube') subdir('Status') diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index d3fd28b57..0269856da 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -184,6 +184,8 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { // .with_param ("code", code_entry.text); // yield token_req.await (); + warning (account.client_secret); + warning (code_entry.text); var token_req = new Request.POST ("/api/auth/session/userkey") .with_account (account) .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_session_userkey (account.client_secret, code_entry.text).data)); @@ -198,7 +200,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { if (account.access_token == null) throw new Oopsie.INSTANCE (_("Instance failed to authorize the access token")); - yield account.verify_credentials (); + yield account.verify_credentials (root.get_member ("user").get_object ().get_string_member ("id")); account = accounts.create_account (account.to_json ()); @@ -216,22 +218,17 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { present (); message (@"Received uri: $t_uri"); - Uri uri; try { - uri = Uri.parse (t_uri, UriFlags.NONE); + var uri = Uri.parse (t_uri, UriFlags.NONE); + warning (uri.get_query ()); + var uri_params = Uri.parse_params (uri.get_query ()); + code_entry.text = uri_params.contains ("code") ? uri_params.get ("code") : ""; + is_working = false; + on_next_clicked (); } catch (GLib.UriError e) { warning (e.message); return; } - - var query = uri.get_query (); - var split = query.split ("="); - var code = split[1]; - var code_clean = code.splice(code.index_of_char('&'), -1); - - code_entry.text = code_clean; - is_working = false; - on_next_clicked (); } public void mark_errors (string error_message) { diff --git a/src/Services/Accounts/AccountStore.vala b/src/Services/Accounts/AccountStore.vala index 9126cc263..3ba95741f 100644 --- a/src/Services/Accounts/AccountStore.vala +++ b/src/Services/Accounts/AccountStore.vala @@ -84,7 +84,7 @@ public abstract class Tuba.AccountStore : GLib.Object { } else { message (@"Activating $(account.handle)..."); - account.verify_credentials.begin ((obj, res) => { + account.verify_credentials.begin (null, (obj, res) => { try { account.verify_credentials.end (res); account.error = null; diff --git a/src/Services/Accounts/InstanceAccount.vala b/src/Services/Accounts/InstanceAccount.vala index c1aa5b681..c0aea5afc 100644 --- a/src/Services/Accounts/InstanceAccount.vala +++ b/src/Services/Accounts/InstanceAccount.vala @@ -121,9 +121,12 @@ public class Tuba.InstanceAccount : API.Account, Streamable { return Entity.from_json (type, node); } - public async void verify_credentials () throws Error { + public async void verify_credentials (string? t_id = null) throws Error { // var req = new Request.GET ("/api/v1/accounts/verify_credentials").with_account (this); - var req = new Request.POST ("/api/show").with_account (this).body ("application/json", new Bytes.take(API.Misskey.JSON.get_show_userid (this.id).data)); + warning (t_id); + var req = new Request.POST ("/api/users/show") + .with_account (this) + .body ("application/json", new Bytes.take(API.Misskey.JSON.get_show_userid (t_id).data)); yield req.await (); var parser = Network.get_parser_from_inputstream(req.response_body); diff --git a/src/Views/Federated.vala b/src/Views/Federated.vala index 759925f75..34f1d1042 100644 --- a/src/Views/Federated.vala +++ b/src/Views/Federated.vala @@ -2,7 +2,7 @@ public class Tuba.Views.Federated : Views.Timeline { public Federated () { Object ( - url: "/api/v1/timelines/public", + url: "/api/notes/global-timeline", is_public: true, label: _("Federated"), icon: "tuba-globe-symbolic" diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index 9e6c7764a..a19d3bfab 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -165,20 +165,41 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase } public virtual bool request () { - append_params (new Request.GET (get_req_url ())) + // append_params (new Request.GET (get_req_url ())) + // .with_account (account) + // .with_ctx (this) + // .then ((sess, msg, in_stream) => { + // var parser = Network.get_parser_from_inputstream(in_stream); + + // Object[] to_add = {}; + // Network.parse_array (msg, parser, node => { + // var e = entity_cache.lookup_or_insert (node, accepts); + // to_add += e; + // }); + // model.splice (model.get_n_items (), 0, to_add); + + // get_pages (msg.response_headers.get_one ("Link")); + // on_content_changed (); + // on_request_finish (); + // }) + // .on_error (on_error) + // .exec (); + + new Request.POST (get_req_url ()) .with_account (account) .with_ctx (this) + .body (Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size)) .then ((sess, msg, in_stream) => { var parser = Network.get_parser_from_inputstream(in_stream); - + Object[] to_add = {}; Network.parse_array (msg, parser, node => { var e = entity_cache.lookup_or_insert (node, accepts); to_add += e; }); model.splice (model.get_n_items (), 0, to_add); - - get_pages (msg.response_headers.get_one ("Link")); + + // get_pages (msg.response_headers.get_one ("Link")); on_content_changed (); on_request_finish (); }) From 6ad5ed7adc766836c216dfc73eabda1fe99944ad Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Mon, 19 Jun 2023 17:54:04 +0300 Subject: [PATCH 03/14] feat: bump - Split namespaces better - Introduce AiChanify interface to convert mk object to masto - Quick start timeline work --- src/API/Entity.vala | 2 +- src/API/Misskey.vala | 258 ++++++++---------- src/API/Misskey/AiChanify.vala | 5 + src/API/Misskey/Emoji.vala | 4 + src/API/Misskey/Entity.vala | 142 ++++++++++ src/API/Misskey/Note.vala | 27 +- src/API/Misskey/User.vala | 52 ++-- src/API/Misskey/meson.build | 4 + src/Dialogs/NewAccount.vala | 2 +- src/Services/Accounts/InstanceAccount.vala | 4 +- src/Services/Accounts/SecretAccountStore.vala | 3 + src/Views/Timeline.vala | 7 +- 12 files changed, 333 insertions(+), 177 deletions(-) create mode 100644 src/API/Misskey/AiChanify.vala create mode 100644 src/API/Misskey/Emoji.vala create mode 100644 src/API/Misskey/Entity.vala diff --git a/src/API/Entity.vala b/src/API/Entity.vala index ea1e1f3e9..ee2063763 100644 --- a/src/API/Entity.vala +++ b/src/API/Entity.vala @@ -6,7 +6,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { return true; } - static bool is_spec_valid (ref ParamSpec spec) { + public static bool is_spec_valid (ref ParamSpec spec) { return ParamFlags.WRITABLE in spec.flags; } diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index b15031ac8..ad883d58e 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -1,117 +1,117 @@ -public class Tuba.API.Misskey : Object { - public class JSON : Object { - public static string get_app (string callback_url) { - var builder = new Json.Builder (); - builder.begin_object (); - builder.set_member_name ("name"); - builder.add_string_value (Build.NAME); - - builder.set_member_name ("description"); - builder.add_string_value ("Browse the Fediverse"); - - builder.set_member_name ("permission"); - builder.begin_array (); - builder.add_string_value ("write:user-groups"); - builder.add_string_value ("read:user-groups"); - builder.add_string_value ("read:page-likes"); - builder.add_string_value ("write:page-likes"); - builder.add_string_value ("write:pages"); - builder.add_string_value ("read:pages"); - builder.add_string_value ("write:votes"); - builder.add_string_value ("write:reactions"); - builder.add_string_value ("read:reactions"); - builder.add_string_value ("write:notifications"); - builder.add_string_value ("read:notifications"); - builder.add_string_value ("write:notes"); - builder.add_string_value ("write:mutes"); - builder.add_string_value ("read:mutes"); - builder.add_string_value ("read:account"); - builder.add_string_value ("write:account"); - builder.add_string_value ("read:blocks"); - builder.add_string_value ("write:blocks"); - builder.add_string_value ("read:drive"); - builder.add_string_value ("write:drive"); - builder.add_string_value ("read:favorites"); - builder.add_string_value ("write:favorites"); - builder.add_string_value ("read:following"); - builder.add_string_value ("write:following"); - builder.add_string_value ("read:messaging"); - builder.add_string_value ("write:messaging"); - builder.end_array (); - - builder.set_member_name ("callbackUrl"); - builder.add_string_value (callback_url); - - builder.end_object (); - - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); - } - - public static string get_session_generate (string secret) { - var builder = new Json.Builder (); - builder.begin_object (); - - builder.set_member_name ("appSecret"); - builder.add_string_value (secret); - - builder.end_object (); - - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); - } - - public static string get_session_userkey (string secret, string token) { - var builder = new Json.Builder (); - builder.begin_object (); - - builder.set_member_name ("appSecret"); - builder.add_string_value (secret); - - builder.set_member_name ("token"); - builder.add_string_value (token); - - builder.end_object (); - - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); - } - - public static string get_show_userid (string userid) { - var builder = new Json.Builder (); - builder.begin_object (); - - builder.set_member_name ("userId"); - builder.add_string_value (userid); - - builder.end_object (); - - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); - } - - public static string get_timeline (int limit = 20) { - var builder = new Json.Builder (); - builder.begin_object (); - - builder.set_member_name ("i"); - builder.add_string_value (accounts.active.i); - - builder.set_member_name ("limit"); - builder.add_int_value (limit); - - builder.end_object (); - - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); - } +public class Tuba.API.Misskey.JSON : Object { + public static string get_app (string callback_url) { + var builder = new Json.Builder (); + builder.begin_object (); + builder.set_member_name ("name"); + builder.add_string_value (Build.NAME); + + builder.set_member_name ("description"); + builder.add_string_value ("Browse the Fediverse"); + + builder.set_member_name ("permission"); + builder.begin_array (); + builder.add_string_value ("write:user-groups"); + builder.add_string_value ("read:user-groups"); + builder.add_string_value ("read:page-likes"); + builder.add_string_value ("write:page-likes"); + builder.add_string_value ("write:pages"); + builder.add_string_value ("read:pages"); + builder.add_string_value ("write:votes"); + builder.add_string_value ("write:reactions"); + builder.add_string_value ("read:reactions"); + builder.add_string_value ("write:notifications"); + builder.add_string_value ("read:notifications"); + builder.add_string_value ("write:notes"); + builder.add_string_value ("write:mutes"); + builder.add_string_value ("read:mutes"); + builder.add_string_value ("read:account"); + builder.add_string_value ("write:account"); + builder.add_string_value ("read:blocks"); + builder.add_string_value ("write:blocks"); + builder.add_string_value ("read:drive"); + builder.add_string_value ("write:drive"); + builder.add_string_value ("read:favorites"); + builder.add_string_value ("write:favorites"); + builder.add_string_value ("read:following"); + builder.add_string_value ("write:following"); + builder.add_string_value ("read:messaging"); + builder.add_string_value ("write:messaging"); + builder.end_array (); + + builder.set_member_name ("callbackUrl"); + builder.add_string_value (callback_url); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); } + public static string get_session_generate (string secret) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("appSecret"); + builder.add_string_value (secret); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_session_userkey (string secret, string token) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("appSecret"); + builder.add_string_value (secret); + + builder.set_member_name ("token"); + builder.add_string_value (token); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_show_userid (string userid) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("userId"); + builder.add_string_value (userid); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } + + public static string get_timeline (int limit = 20) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("i"); + builder.add_string_value (accounts.active.i); + + builder.set_member_name ("limit"); + builder.add_int_value (limit); + + builder.end_object (); + + var generator = new Json.Generator (); + generator.set_root (builder.get_root ()); + return generator.to_data (null); + } +} + +public class Tuba.API.Misskey.Utils : Object { public static string generate_i (string secret, string access_token) { string pre_c = @"$access_token$secret"; Checksum checksum = new Checksum (ChecksumType.SHA256); @@ -119,38 +119,4 @@ public class Tuba.API.Misskey : Object { checksum.update (pre_c.data, -1); return checksum.get_string (); } - - public static API.Account to_mastodon_account (Json.Node node) { - var root = node.get_object (); - - var mk_name = root.get_string_member ("name"); - var mk_username = root.get_string_member ("username"); - var mk_host = root.get_string_member ("host"); - var mk_avatar_url = root.get_string_member ("avatarUrl"); - var mk_description = root.get_string_member ("description"); - var mk_is_locked = root.get_boolean_member ("isLocked"); - var mk_url = root.get_string_member ("url"); - var mk_created_at = root.get_string_member ("createdAt"); - var mk_banner_url = root.get_string_member ("bannerUrl"); - var mk_followers_count = root.get_int_member ("followersCount"); - var mk_following_count = root.get_int_member ("followingCount"); - var mk_notes_count = root.get_int_member ("notesCount"); - // fields - // pinned - - var masto_acc = new API.Account.empty (root.get_string_member ("id")); - masto_acc.username = mk_username; - masto_acc.acct = mk_host != null ? @"$mk_username@$mk_host" : mk_username; - masto_acc.note = mk_description ?? ""; - masto_acc.locked = mk_is_locked; - masto_acc.header = mk_banner_url; - masto_acc.avatar = mk_avatar_url; - masto_acc.url = mk_url; - masto_acc.followers_count = mk_followers_count; - masto_acc.following_count = mk_following_count; - masto_acc.statuses_count = mk_notes_count; - masto_acc.created_at = mk_created_at; - - return masto_acc; - } } diff --git a/src/API/Misskey/AiChanify.vala b/src/API/Misskey/AiChanify.vala new file mode 100644 index 000000000..96e7ef5c3 --- /dev/null +++ b/src/API/Misskey/AiChanify.vala @@ -0,0 +1,5 @@ +public interface Tuba.API.Misskey.AiChanify : GLib.Object { + public virtual Entity to_mastodon () throws Oopsie { + throw new Tuba.Oopsie.INTERNAL ("Ai Chan didn't provide a Mastodon entity!"); + } +} diff --git a/src/API/Misskey/Emoji.vala b/src/API/Misskey/Emoji.vala new file mode 100644 index 000000000..627ee3649 --- /dev/null +++ b/src/API/Misskey/Emoji.vala @@ -0,0 +1,4 @@ +public class Tuba.API.Misskey.Emoji : Entity { + public string name { get; set; } + public string url { get; set; } +} diff --git a/src/API/Misskey/Entity.vala b/src/API/Misskey/Entity.vala new file mode 100644 index 000000000..554657ac4 --- /dev/null +++ b/src/API/Misskey/Entity.vala @@ -0,0 +1,142 @@ +using Json; + +public class Tuba.Misskey.Entity : Tuba.Entity, Json.Serializable { + static bool is_spec_valid (ref ParamSpec spec) { + return Tuba.Entity.is_spec_valid (ref spec); + } + + public override unowned ParamSpec? find_property (string name) { + switch (name) { + case "type": + return get_class ().find_property ("kind"); + case "value": + return get_class ().find_property ("val"); + default: + return get_class ().find_property (name); + } + } + + public static Tuba.Misskey.Entity from_json (Type type, Json.Node node) throws Error { + var obj = node.get_object (); + if (obj == null) + throw new Oopsie.PARSING (@"Received Json.Node for $(type.name ()) is not a Json.Object!"); + + return Json.gobject_deserialize (type, node) as Entity; + } + + public Json.Node to_json () { + return Json.gobject_serialize (this); + } + + public string to_json_data () { + size_t len; + return Json.gobject_to_data (this, out len); + } + + public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) { + var success = default_deserialize_property (prop, out val, spec, node); + + var type = spec.value_type; + if (val.type () == Type.INVALID) { // Fix for glib-json < 1.5.1 + val.init (type); + spec.set_value_default (ref val); + type = spec.value_type; + } + + if (type.is_a (typeof (Gee.ArrayList))) { + Type contains; + + //There has to be a better way + switch (prop) { + case "mentions": + return des_list_string(out val, node); + case "emojis": + contains = typeof (API.Misskey.Emoji); + break; + default: + contains = typeof (Entity); + break; + } + return des_list (out val, node, contains); + } + + if (type.is_a (typeof (Gee.HashMap))) { + Type contains; + + switch (prop) { + case "mentions": + return des_map_string_string(out val, node); + default: + contains = typeof (Entity); + break; + } + return des_map (out val, node, contains); + } + + return success; + } + + public static bool des_list (out Value val, Json.Node node, Type type) { + Tuba.Entity.des_list (out val, node, type); + return true; + } + + public static bool des_list_string (out Value val, Json.Node node) { + Tuba.Entity.des_list_string (out val, node); + return true; + } + + public static bool des_map_string_string (out Value val, Json.Node node) { + var map = new Gee.HashMap (); + if (!node.is_null ()) { + node.get_object ().foreach_member((obj, t_key, t_node) => { + map.set (t_key, (string) t_node.get_string ()); + }); + } + val = map; + return true; + } + + public static bool des_map (out Value val, Json.Node node, Type type) { + var map = new Gee.HashMap (); + if (!node.is_null ()) { + node.get_object ().foreach_member((obj, t_key, t_node) => { + try { + var t_obj = Entity.from_json (type, t_node); + map.set (t_key, t_obj); + } catch (Error e) { + warning (@"Error getting Entity from json: $(e.message)"); + } + }); + } + val = map; + return true; + } + + public override Json.Node serialize_property (string prop, Value val, ParamSpec spec) { + var type = spec.value_type; + // debug (@"serializing $prop of type $(val.type_name ())"); + + if (type.is_a (typeof (Gee.ArrayList))) + return ser_list (prop, val, spec); + + return default_serialize_property (prop, val, spec); + } + + static Json.Node ser_list (string prop, Value val, ParamSpec spec) { + var list = (Gee.ArrayList) val; + if (list == null) + return new Json.Node (NodeType.NULL); + + var arr = new Json.Array (); + list.@foreach (e => { + var enode = e.to_json (); + arr.add_element (enode); + return true; + }); + + var node = new Json.Node (NodeType.ARRAY); + node.set_array (arr); + return node; + } +} diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index c9d1445cf..d401e8998 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -1,4 +1,4 @@ -public class Tuba.API.Misskey.Note : Entity { +public class Tuba.API.Misskey.Note : Tuba.Misskey.Entity, AiChanify { ~Note () { message ("[OBJ] Destroyed "+uri); } @@ -7,16 +7,31 @@ public class Tuba.API.Misskey.Note : Entity { public string createdAt { get; set; default = "0"; } public string text { get; set; default = ""; } public string? cw { get; set; default = null; } - public API.Account user { get; set; } + public API.Misskey.User user { get; set; } public string? replyId { get; set; default = null; } public string visibility { get; set; default = settings.default_post_visibility; } - public Gee.ArrayList? mentions { get; set; default = null; } - public API.Poll? poll { get; set; default = null; } - public Gee.ArrayList? emojis { get; set; } - public Gee.ArrayList? reactions { get; set; default = null; } + public Gee.ArrayList? mentions { get; set; default = null; } + // public API.Poll? poll { get; set; default = null; } + public Gee.ArrayList? emojis { get; set; } + public Gee.HashMap? reactions { get; set; default = null; } public int64 renotesCount { get; set; default = 0; } public int64 repliesCount { get; set; default = 0; } public string uri { get; set; } public string url { get; set; } public API.Misskey.Note? renote { get; set; default = null; } + + public override Entity to_mastodon () { + var masto_status = new API.Status.empty (); + masto_status.id = id; + masto_status.account = (API.Account) user.to_mastodon (); + masto_status.spoiler_text = cw; + masto_status.in_reply_to_id = replyId; + masto_status.content = text; + masto_status.replies_count = repliesCount; + masto_status.reblogs_count = renotesCount; + masto_status.visibility = visibility; + masto_status.uri = uri; + + return masto_status; + } } diff --git a/src/API/Misskey/User.vala b/src/API/Misskey/User.vala index c828c3b7a..19da0a457 100644 --- a/src/API/Misskey/User.vala +++ b/src/API/Misskey/User.vala @@ -1,22 +1,36 @@ -public class Tuba.API.Misskey.User : Entity { - ~Note () { - message ("[OBJ] Destroyed "+uri); - } - +public class Tuba.API.Misskey.User : Tuba.Misskey.Entity, AiChanify { public string id { get; set; } - public string createdAt { get; set; default = "0"; } - public string text { get; set; default = ""; } - public string? cw { get; set; default = null; } - public API.Account user { get; set; } - public string? replyId { get; set; default = null; } - public string visibility { get; set; default = settings.default_post_visibility; } - public Gee.ArrayList? mentions { get; set; default = null; } - public API.Poll? poll { get; set; default = null; } - public Gee.ArrayList? emojis { get; set; } - public Gee.ArrayList? reactions { get; set; default = null; } - public int64 renotesCount { get; set; default = 0; } - public int64 repliesCount { get; set; default = 0; } - public string uri { get; set; } + public string name { get; set; } + public string username { get; set; } + public string host { get; set; } + public string avatarUrl { get; set; } + public string description { get; set; } + public bool isLocked { get; set; } public string url { get; set; } - public API.Misskey.Note? renote { get; set; default = null; } + public string createdAt { get; set; } + public string bannerUrl { get; set; } + public int64 followersCount { get; set; } + public int64 followingCount { get; set; } + public int64 notesCount { get; set; } + + public static User from (Json.Node node) throws Error { + return Tuba.Misskey.Entity.from_json (typeof (API.Misskey.User), node) as API.Misskey.User; + } + + public override Entity to_mastodon () { + var masto_acc = new API.Account.empty (id); + masto_acc.username = username; + masto_acc.acct = host != null ? @"$username@$host" : username; + masto_acc.note = description ?? ""; + masto_acc.locked = isLocked; + masto_acc.header = bannerUrl; + masto_acc.avatar = avatarUrl; + masto_acc.url = url ?? @"$host/@$username"; + masto_acc.followers_count = followersCount; + masto_acc.following_count = followingCount; + masto_acc.statuses_count = notesCount; + masto_acc.created_at = createdAt; + + return masto_acc; + } } diff --git a/src/API/Misskey/meson.build b/src/API/Misskey/meson.build index caf1f5525..b5912b2b1 100644 --- a/src/API/Misskey/meson.build +++ b/src/API/Misskey/meson.build @@ -1,3 +1,7 @@ sources += files( + 'AiChanify.vala', + 'Emoji.vala', + 'Entity.vala', 'Note.vala', + 'User.vala', ) \ No newline at end of file diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index 0269856da..6c12324fc 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -195,7 +195,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { var root = network.parse (parser); // account.access_token = root.get_string_member ("access_token"); account.access_token = root.get_string_member ("accessToken"); - account.i = API.Misskey.generate_i (account.access_token, account.client_secret); + account.i = API.Misskey.Utils.generate_i (account.access_token, account.client_secret); if (account.access_token == null) throw new Oopsie.INSTANCE (_("Instance failed to authorize the access token")); diff --git a/src/Services/Accounts/InstanceAccount.vala b/src/Services/Accounts/InstanceAccount.vala index c0aea5afc..725b983fc 100644 --- a/src/Services/Accounts/InstanceAccount.vala +++ b/src/Services/Accounts/InstanceAccount.vala @@ -132,7 +132,9 @@ public class Tuba.InstanceAccount : API.Account, Streamable { var parser = Network.get_parser_from_inputstream(req.response_body); var node = network.parse_node (parser); // var updated = API.Account.from (node); - var updated = API.Misskey.to_mastodon_account (node); + var mk_account = API.Misskey.User.from (node); + mk_account.host = this.instance; + var updated = (API.Account) mk_account.to_mastodon (); patch (updated); message (@"$handle: profile updated"); diff --git a/src/Services/Accounts/SecretAccountStore.vala b/src/Services/Accounts/SecretAccountStore.vala index b4fae0119..93b3339b7 100644 --- a/src/Services/Accounts/SecretAccountStore.vala +++ b/src/Services/Accounts/SecretAccountStore.vala @@ -124,6 +124,9 @@ public class Tuba.SecretAccountStore : AccountStore { var generator = new Json.Generator (); account.instance_info = null; account.instance_emojis = null; + account.t_connection_url = null; + account.source = null; + generator.set_root (account.to_json ()); var secret = generator.to_data (null); // translators: The variable is the backend like "Mastodon" diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index a19d3bfab..e1fa080ed 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -188,14 +188,15 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase new Request.POST (get_req_url ()) .with_account (account) .with_ctx (this) - .body (Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size)) + .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size).data)) .then ((sess, msg, in_stream) => { var parser = Network.get_parser_from_inputstream(in_stream); Object[] to_add = {}; Network.parse_array (msg, parser, node => { - var e = entity_cache.lookup_or_insert (node, accepts); - to_add += e; + var e = entity_cache.lookup_or_insert (node, typeof (API.Misskey.Note)); + + to_add += (API.Status) ((API.Misskey.Note) e).to_mastodon (); }); model.splice (model.get_n_items (), 0, to_add); From 8a321ee765a3b59cfa6f3e8f593550c268ff1f03 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Mon, 26 Jun 2023 02:11:04 +0300 Subject: [PATCH 04/14] feat: bump - use body_json since it was introduced on main - attempt to clean inheritance & deserialization - do not use `i` for auth, instead use bearer --- src/API/Entity.vala | 27 ++++ src/API/Misskey.vala | 34 ++--- src/API/Misskey/Entity.vala | 142 --------------------- src/API/Misskey/Note.vala | 42 +++++- src/API/Misskey/User.vala | 4 +- src/API/Misskey/meson.build | 1 - src/Dialogs/NewAccount.vala | 6 +- src/Services/Accounts/InstanceAccount.vala | 2 +- src/Views/Timeline.vala | 2 +- 9 files changed, 87 insertions(+), 173 deletions(-) delete mode 100644 src/API/Misskey/Entity.vala diff --git a/src/API/Entity.vala b/src/API/Entity.vala index ee2063763..e06d45e23 100644 --- a/src/API/Entity.vala +++ b/src/API/Entity.vala @@ -178,4 +178,31 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { return node; } + public static bool des_map_string_string (out Value val, Json.Node node) { + var map = new Gee.HashMap (); + if (!node.is_null ()) { + node.get_object ().foreach_member((obj, t_key, t_node) => { + map.set (t_key, (string) t_node.get_string ()); + }); + } + val = map; + return true; + } + + public static bool des_map (out Value val, Json.Node node, Type type) { + var map = new Gee.HashMap (); + if (!node.is_null ()) { + node.get_object ().foreach_member((obj, t_key, t_node) => { + try { + var t_obj = Entity.from_json (type, t_node); + map.set (t_key, t_obj); + } catch (Error e) { + warning (@"Error getting Entity from json: $(e.message)"); + } + }); + } + val = map; + return true; + } + } diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index ad883d58e..05745a843 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -1,5 +1,5 @@ public class Tuba.API.Misskey.JSON : Object { - public static string get_app (string callback_url) { + public static Json.Builder get_app (string callback_url) { var builder = new Json.Builder (); builder.begin_object (); builder.set_member_name ("name"); @@ -43,12 +43,10 @@ public class Tuba.API.Misskey.JSON : Object { builder.end_object (); - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); + return builder; } - public static string get_session_generate (string secret) { + public static Json.Builder get_session_generate (string secret) { var builder = new Json.Builder (); builder.begin_object (); @@ -57,12 +55,10 @@ public class Tuba.API.Misskey.JSON : Object { builder.end_object (); - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); + return builder; } - public static string get_session_userkey (string secret, string token) { + public static Json.Builder get_session_userkey (string secret, string token) { var builder = new Json.Builder (); builder.begin_object (); @@ -74,12 +70,10 @@ public class Tuba.API.Misskey.JSON : Object { builder.end_object (); - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); + return builder; } - public static string get_show_userid (string userid) { + public static Json.Builder get_show_userid (string userid) { var builder = new Json.Builder (); builder.begin_object (); @@ -88,26 +82,22 @@ public class Tuba.API.Misskey.JSON : Object { builder.end_object (); - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); + return builder; } - public static string get_timeline (int limit = 20) { + public static Json.Builder get_timeline (int limit = 20) { var builder = new Json.Builder (); builder.begin_object (); - builder.set_member_name ("i"); - builder.add_string_value (accounts.active.i); + // builder.set_member_name ("i"); + // builder.add_string_value (accounts.active.i); builder.set_member_name ("limit"); builder.add_int_value (limit); builder.end_object (); - var generator = new Json.Generator (); - generator.set_root (builder.get_root ()); - return generator.to_data (null); + return builder; } } diff --git a/src/API/Misskey/Entity.vala b/src/API/Misskey/Entity.vala deleted file mode 100644 index 554657ac4..000000000 --- a/src/API/Misskey/Entity.vala +++ /dev/null @@ -1,142 +0,0 @@ -using Json; - -public class Tuba.Misskey.Entity : Tuba.Entity, Json.Serializable { - static bool is_spec_valid (ref ParamSpec spec) { - return Tuba.Entity.is_spec_valid (ref spec); - } - - public override unowned ParamSpec? find_property (string name) { - switch (name) { - case "type": - return get_class ().find_property ("kind"); - case "value": - return get_class ().find_property ("val"); - default: - return get_class ().find_property (name); - } - } - - public static Tuba.Misskey.Entity from_json (Type type, Json.Node node) throws Error { - var obj = node.get_object (); - if (obj == null) - throw new Oopsie.PARSING (@"Received Json.Node for $(type.name ()) is not a Json.Object!"); - - return Json.gobject_deserialize (type, node) as Entity; - } - - public Json.Node to_json () { - return Json.gobject_serialize (this); - } - - public string to_json_data () { - size_t len; - return Json.gobject_to_data (this, out len); - } - - public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) { - var success = default_deserialize_property (prop, out val, spec, node); - - var type = spec.value_type; - if (val.type () == Type.INVALID) { // Fix for glib-json < 1.5.1 - val.init (type); - spec.set_value_default (ref val); - type = spec.value_type; - } - - if (type.is_a (typeof (Gee.ArrayList))) { - Type contains; - - //There has to be a better way - switch (prop) { - case "mentions": - return des_list_string(out val, node); - case "emojis": - contains = typeof (API.Misskey.Emoji); - break; - default: - contains = typeof (Entity); - break; - } - return des_list (out val, node, contains); - } - - if (type.is_a (typeof (Gee.HashMap))) { - Type contains; - - switch (prop) { - case "mentions": - return des_map_string_string(out val, node); - default: - contains = typeof (Entity); - break; - } - return des_map (out val, node, contains); - } - - return success; - } - - public static bool des_list (out Value val, Json.Node node, Type type) { - Tuba.Entity.des_list (out val, node, type); - return true; - } - - public static bool des_list_string (out Value val, Json.Node node) { - Tuba.Entity.des_list_string (out val, node); - return true; - } - - public static bool des_map_string_string (out Value val, Json.Node node) { - var map = new Gee.HashMap (); - if (!node.is_null ()) { - node.get_object ().foreach_member((obj, t_key, t_node) => { - map.set (t_key, (string) t_node.get_string ()); - }); - } - val = map; - return true; - } - - public static bool des_map (out Value val, Json.Node node, Type type) { - var map = new Gee.HashMap (); - if (!node.is_null ()) { - node.get_object ().foreach_member((obj, t_key, t_node) => { - try { - var t_obj = Entity.from_json (type, t_node); - map.set (t_key, t_obj); - } catch (Error e) { - warning (@"Error getting Entity from json: $(e.message)"); - } - }); - } - val = map; - return true; - } - - public override Json.Node serialize_property (string prop, Value val, ParamSpec spec) { - var type = spec.value_type; - // debug (@"serializing $prop of type $(val.type_name ())"); - - if (type.is_a (typeof (Gee.ArrayList))) - return ser_list (prop, val, spec); - - return default_serialize_property (prop, val, spec); - } - - static Json.Node ser_list (string prop, Value val, ParamSpec spec) { - var list = (Gee.ArrayList) val; - if (list == null) - return new Json.Node (NodeType.NULL); - - var arr = new Json.Array (); - list.@foreach (e => { - var enode = e.to_json (); - arr.add_element (enode); - return true; - }); - - var node = new Json.Node (NodeType.ARRAY); - node.set_array (arr); - return node; - } -} diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index d401e8998..34ffd5f25 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -1,4 +1,4 @@ -public class Tuba.API.Misskey.Note : Tuba.Misskey.Entity, AiChanify { +public class Tuba.API.Misskey.Note : Entity, Json.Serializable, AiChanify { ~Note () { message ("[OBJ] Destroyed "+uri); } @@ -20,6 +20,41 @@ public class Tuba.API.Misskey.Note : Tuba.Misskey.Entity, AiChanify { public string url { get; set; } public API.Misskey.Note? renote { get; set; default = null; } + public static Note from (Json.Node node) throws Error { + return Entity.from_json (typeof (API.Misskey.Note), node) as API.Misskey.Note; + } + + public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) { + var success = default_deserialize_property (prop, out val, spec, node); + + var type = spec.value_type; + if (val.type () == Type.INVALID) { + val.init (type); + spec.set_value_default (ref val); + type = spec.value_type; + } + + if (type.is_a (typeof (Gee.ArrayList))) { + Type contains; + + switch (prop) { + case "mentions": + return Entity.des_list_string(out val, node); + case "emojis": + contains = typeof (API.Misskey.Emoji); + break; + case "reactions": + return Entity.des_map_string_string (out val, node); + default: + contains = typeof (Entity); + break; + } + return des_list (out val, node, contains); + } + + return success; + } + public override Entity to_mastodon () { var masto_status = new API.Status.empty (); masto_status.id = id; @@ -31,6 +66,11 @@ public class Tuba.API.Misskey.Note : Tuba.Misskey.Entity, AiChanify { masto_status.reblogs_count = renotesCount; masto_status.visibility = visibility; masto_status.uri = uri; + + if (renote != null) { + var reblog_status = renote.to_mastodon () as API.Status; + masto_status.reblog = reblog_status; + } return masto_status; } diff --git a/src/API/Misskey/User.vala b/src/API/Misskey/User.vala index 19da0a457..3fcb2ebfd 100644 --- a/src/API/Misskey/User.vala +++ b/src/API/Misskey/User.vala @@ -1,4 +1,4 @@ -public class Tuba.API.Misskey.User : Tuba.Misskey.Entity, AiChanify { +public class Tuba.API.Misskey.User : Entity, AiChanify, Json.Serializable { public string id { get; set; } public string name { get; set; } public string username { get; set; } @@ -14,7 +14,7 @@ public class Tuba.API.Misskey.User : Tuba.Misskey.Entity, AiChanify { public int64 notesCount { get; set; } public static User from (Json.Node node) throws Error { - return Tuba.Misskey.Entity.from_json (typeof (API.Misskey.User), node) as API.Misskey.User; + return Entity.from_json (typeof (API.Misskey.User), node) as API.Misskey.User; } public override Entity to_mastodon () { diff --git a/src/API/Misskey/meson.build b/src/API/Misskey/meson.build index b5912b2b1..ac07ed8f8 100644 --- a/src/API/Misskey/meson.build +++ b/src/API/Misskey/meson.build @@ -1,7 +1,6 @@ sources += files( 'AiChanify.vala', 'Emoji.vala', - 'Entity.vala', 'Note.vala', 'User.vala', ) \ No newline at end of file diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index 6c12324fc..81973bc56 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -133,7 +133,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { var msg = new Request.POST ("/api/app/create") .with_account (account) - .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_app (setup_redirect_uri ()).data)); + .body_json (Tuba.API.Misskey.JSON.get_app (setup_redirect_uri ())); yield msg.await (); var parser = Network.get_parser_from_inputstream(msg.response_body); @@ -149,7 +149,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { var mk_msg = new Request.POST ("/api/auth/session/generate") .with_account (account) - .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_session_generate (account.client_secret).data)); + .body_json (Tuba.API.Misskey.JSON.get_session_generate (account.client_secret)); yield mk_msg.await (); parser = Network.get_parser_from_inputstream(mk_msg.response_body); @@ -188,7 +188,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { warning (code_entry.text); var token_req = new Request.POST ("/api/auth/session/userkey") .with_account (account) - .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_session_userkey (account.client_secret, code_entry.text).data)); + .body_json (Tuba.API.Misskey.JSON.get_session_userkey (account.client_secret, code_entry.text)); yield token_req.await (); var parser = Network.get_parser_from_inputstream(token_req.response_body); diff --git a/src/Services/Accounts/InstanceAccount.vala b/src/Services/Accounts/InstanceAccount.vala index 725b983fc..6db923003 100644 --- a/src/Services/Accounts/InstanceAccount.vala +++ b/src/Services/Accounts/InstanceAccount.vala @@ -126,7 +126,7 @@ public class Tuba.InstanceAccount : API.Account, Streamable { warning (t_id); var req = new Request.POST ("/api/users/show") .with_account (this) - .body ("application/json", new Bytes.take(API.Misskey.JSON.get_show_userid (t_id).data)); + .body_json (API.Misskey.JSON.get_show_userid (t_id)); yield req.await (); var parser = Network.get_parser_from_inputstream(req.response_body); diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index e1fa080ed..195b21942 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -188,7 +188,7 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase new Request.POST (get_req_url ()) .with_account (account) .with_ctx (this) - .body ("application/json", new Bytes.take(Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size).data)) + .body_json (Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size)) .then ((sess, msg, in_stream) => { var parser = Network.get_parser_from_inputstream(in_stream); From 17ff0f8823854464b708af8965cbb5610a940a08 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Tue, 27 Jun 2023 02:39:00 +0300 Subject: [PATCH 05/14] feat: bump mk: get_children timeline: home, local, notifications, bookmarks --- src/API/Misskey.vala | 15 +++++++++++++++ src/API/Misskey/Bookmark.vala | 3 +++ src/Views/Bookmarks.vala | 2 +- src/Views/Home.vala | 2 +- src/Views/Local.vala | 25 ++++++++++++++----------- src/Views/Notifications.vala | 2 +- 6 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 src/API/Misskey/Bookmark.vala diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index 05745a843..83390321f 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -99,6 +99,21 @@ public class Tuba.API.Misskey.JSON : Object { return builder; } + + public static Json.Builder get_children (string noteId) { + var builder = new Json.Builder (); + builder.begin_object (); + + // builder.set_member_name ("limit"); + // builder.add_int_value (limit); + + builder.set_member_name ("noteId"); + builder.add_string_value (noteId); + + builder.end_object (); + + return builder; + } } public class Tuba.API.Misskey.Utils : Object { diff --git a/src/API/Misskey/Bookmark.vala b/src/API/Misskey/Bookmark.vala new file mode 100644 index 000000000..3c7f0255f --- /dev/null +++ b/src/API/Misskey/Bookmark.vala @@ -0,0 +1,3 @@ +public class Tuba.API.Misskey.Bookmark : Entity { + public API.Misskey.Note note { get; set; } +} diff --git a/src/Views/Bookmarks.vala b/src/Views/Bookmarks.vala index 9b37be05d..4671ea8ae 100644 --- a/src/Views/Bookmarks.vala +++ b/src/Views/Bookmarks.vala @@ -2,7 +2,7 @@ public class Tuba.Views.Bookmarks : Views.Timeline { public Bookmarks () { Object ( - url: "/api/v1/bookmarks", + url: "/api/i/favorites", label: _("Bookmarks"), icon: "tuba-bookmarks-symbolic" ); diff --git a/src/Views/Home.vala b/src/Views/Home.vala index fae42d92c..4b70d4f97 100644 --- a/src/Views/Home.vala +++ b/src/Views/Home.vala @@ -1,7 +1,7 @@ public class Tuba.Views.Home : Views.Timeline { public Home () { Object ( - url: "/api/v1/timelines/home", + url: "/api/notes/timeline", label: _("Home"), icon: "tuba-home-symbolic" ); diff --git a/src/Views/Local.vala b/src/Views/Local.vala index efc3639eb..8a24bb4f5 100644 --- a/src/Views/Local.vala +++ b/src/Views/Local.vala @@ -1,15 +1,18 @@ -public class Tuba.Views.Local : Views.Federated { +public class Tuba.Views.Local : Views.Timeline { + public Local () { + Object ( + url: "/api/notes/local-timeline", + is_public: true, + label: _("Local"), + icon: "tuba-network-server-symbolic" + ); + } - public Local () { - label = _("Local"); - icon = "tuba-network-server-symbolic"; - } - - public override Request append_params (Request r) { - var req = base.append_params (r); - req.with_param ("local", "true"); - return req; - } + // public override Request append_params (Request r) { + // var req = base.append_params (r); + // req.with_param ("local", "true"); + // return req; + // } public override string? get_stream_url () { return account != null ? @"$(account.instance)/api/v1/streaming/?stream=public:local&access_token=$(account.access_token)" : null; diff --git a/src/Views/Notifications.vala b/src/Views/Notifications.vala index 1c3c7c963..5638e84a6 100644 --- a/src/Views/Notifications.vala +++ b/src/Views/Notifications.vala @@ -8,7 +8,7 @@ public class Tuba.Views.Notifications : Views.Timeline, AccountHolder, Streamabl private Binding badge_number_binding; public Notifications () { Object ( - url: "/api/v1/notifications", + url: "/api/i/notifications", label: _("Notifications"), icon: "tuba-bell-symbolic", badge_number: 0, From f07be85bf59886ee60cddda56f90ab4d0128a5c4 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Wed, 28 Jun 2023 05:09:43 +0300 Subject: [PATCH 06/14] feat: bump - finish bookamrks - allow other types - update accepts --- src/API/Misskey/Bookmark.vala | 6 +++++- src/API/Misskey/meson.build | 1 + src/Views/Bookmarks.vala | 1 + src/Views/Federated.vala | 1 + src/Views/Home.vala | 1 + src/Views/Local.vala | 1 + src/Views/Timeline.vala | 8 ++++++-- 7 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/API/Misskey/Bookmark.vala b/src/API/Misskey/Bookmark.vala index 3c7f0255f..45f7cfca5 100644 --- a/src/API/Misskey/Bookmark.vala +++ b/src/API/Misskey/Bookmark.vala @@ -1,3 +1,7 @@ -public class Tuba.API.Misskey.Bookmark : Entity { +public class Tuba.API.Misskey.Bookmark : Entity, AiChanify { public API.Misskey.Note note { get; set; } + + public override Entity to_mastodon () { + return note.to_mastodon (); + } } diff --git a/src/API/Misskey/meson.build b/src/API/Misskey/meson.build index ac07ed8f8..face1b883 100644 --- a/src/API/Misskey/meson.build +++ b/src/API/Misskey/meson.build @@ -1,5 +1,6 @@ sources += files( 'AiChanify.vala', + 'Bookmark.vala', 'Emoji.vala', 'Note.vala', 'User.vala', diff --git a/src/Views/Bookmarks.vala b/src/Views/Bookmarks.vala index 4671ea8ae..680e0cadb 100644 --- a/src/Views/Bookmarks.vala +++ b/src/Views/Bookmarks.vala @@ -6,6 +6,7 @@ public class Tuba.Views.Bookmarks : Views.Timeline { label: _("Bookmarks"), icon: "tuba-bookmarks-symbolic" ); + accepts = typeof (API.Misskey.Bookmark); } } diff --git a/src/Views/Federated.vala b/src/Views/Federated.vala index 34f1d1042..c667cda48 100644 --- a/src/Views/Federated.vala +++ b/src/Views/Federated.vala @@ -7,6 +7,7 @@ public class Tuba.Views.Federated : Views.Timeline { label: _("Federated"), icon: "tuba-globe-symbolic" ); + accepts = typeof (API.Misskey.Note); } public override string? get_stream_url () { diff --git a/src/Views/Home.vala b/src/Views/Home.vala index 4b70d4f97..d0393585c 100644 --- a/src/Views/Home.vala +++ b/src/Views/Home.vala @@ -5,6 +5,7 @@ public class Tuba.Views.Home : Views.Timeline { label: _("Home"), icon: "tuba-home-symbolic" ); + accepts = typeof (API.Misskey.Note); } public override string? get_stream_url () { diff --git a/src/Views/Local.vala b/src/Views/Local.vala index 8a24bb4f5..1894fec0b 100644 --- a/src/Views/Local.vala +++ b/src/Views/Local.vala @@ -6,6 +6,7 @@ public class Tuba.Views.Local : Views.Timeline { label: _("Local"), icon: "tuba-network-server-symbolic" ); + accepts = typeof (API.Misskey.Note); } // public override Request append_params (Request r) { diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index 195b21942..5067f0c07 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -194,9 +194,13 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase Object[] to_add = {}; Network.parse_array (msg, parser, node => { - var e = entity_cache.lookup_or_insert (node, typeof (API.Misskey.Note)); + var e = entity_cache.lookup_or_insert (node, accepts); - to_add += (API.Status) ((API.Misskey.Note) e).to_mastodon (); + if (accepts == typeof (API.Misskey.Bookmark)) { + to_add += (API.Status) ((API.Misskey.Bookmark) e).to_mastodon (); + } else if (accepts == typeof (API.Misskey.Note)) { + to_add += (API.Status) ((API.Misskey.Note) e).to_mastodon (); + } }); model.splice (model.get_n_items (), 0, to_add); From 5624d4c433304c4cc2cd9796ef56f392874cc71f Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 6 Jul 2023 23:54:28 +0300 Subject: [PATCH 07/14] feat(Timeline): use aichanify --- src/Views/Timeline.vala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index 5067f0c07..3fe8190a0 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -196,10 +196,9 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase Network.parse_array (msg, parser, node => { var e = entity_cache.lookup_or_insert (node, accepts); - if (accepts == typeof (API.Misskey.Bookmark)) { - to_add += (API.Status) ((API.Misskey.Bookmark) e).to_mastodon (); - } else if (accepts == typeof (API.Misskey.Note)) { - to_add += (API.Status) ((API.Misskey.Note) e).to_mastodon (); + var e_aichanified = e as API.Misskey.AiChanify; + if (e_aichanified != null) { + to_add += (Entity) e_aichanified.to_mastodon (); } }); model.splice (model.get_n_items (), 0, to_add); From 5b577c59d0d141ddd75b72406b0338bb5b0d5f7e Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Thu, 6 Jul 2023 23:55:02 +0300 Subject: [PATCH 08/14] feat(Note): pass createdAt --- src/API/Misskey/Note.vala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index 34ffd5f25..4df641172 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -4,7 +4,7 @@ public class Tuba.API.Misskey.Note : Entity, Json.Serializable, AiChanify { } public string id { get; set; } - public string createdAt { get; set; default = "0"; } + public string createdAt { get; set; default = ""; } public string text { get; set; default = ""; } public string? cw { get; set; default = null; } public API.Misskey.User user { get; set; } @@ -66,6 +66,7 @@ public class Tuba.API.Misskey.Note : Entity, Json.Serializable, AiChanify { masto_status.reblogs_count = renotesCount; masto_status.visibility = visibility; masto_status.uri = uri; + masto_status.created_at = createdAt; if (renote != null) { var reblog_status = renote.to_mastodon () as API.Status; From 88aa76f1066272767c760fdb762e78cd2afd0ee3 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 00:35:32 +0300 Subject: [PATCH 09/14] feat(API): override to_widget for mk objects --- src/API/Misskey/AiChanify.vala | 2 +- src/API/Misskey/Bookmark.vala | 6 +++++- src/API/Misskey/Note.vala | 6 +++++- src/API/Misskey/User.vala | 6 +++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/API/Misskey/AiChanify.vala b/src/API/Misskey/AiChanify.vala index 96e7ef5c3..fdbbe4a7b 100644 --- a/src/API/Misskey/AiChanify.vala +++ b/src/API/Misskey/AiChanify.vala @@ -1,4 +1,4 @@ -public interface Tuba.API.Misskey.AiChanify : GLib.Object { +public interface Tuba.API.Misskey.AiChanify : GLib.Object, Widgetizable { public virtual Entity to_mastodon () throws Oopsie { throw new Tuba.Oopsie.INTERNAL ("Ai Chan didn't provide a Mastodon entity!"); } diff --git a/src/API/Misskey/Bookmark.vala b/src/API/Misskey/Bookmark.vala index 45f7cfca5..f4b76566e 100644 --- a/src/API/Misskey/Bookmark.vala +++ b/src/API/Misskey/Bookmark.vala @@ -1,7 +1,11 @@ -public class Tuba.API.Misskey.Bookmark : Entity, AiChanify { +public class Tuba.API.Misskey.Bookmark : Entity, Widgetizable, AiChanify { public API.Misskey.Note note { get; set; } public override Entity to_mastodon () { return note.to_mastodon (); } + + public override Gtk.Widget to_widget () { + return to_mastodon ().to_widget (); + } } diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index 4df641172..b94cadfb8 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -1,4 +1,4 @@ -public class Tuba.API.Misskey.Note : Entity, Json.Serializable, AiChanify { +public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, AiChanify { ~Note () { message ("[OBJ] Destroyed "+uri); } @@ -75,4 +75,8 @@ public class Tuba.API.Misskey.Note : Entity, Json.Serializable, AiChanify { return masto_status; } + + public override Gtk.Widget to_widget () { + return to_mastodon ().to_widget (); + } } diff --git a/src/API/Misskey/User.vala b/src/API/Misskey/User.vala index 3fcb2ebfd..50134e26e 100644 --- a/src/API/Misskey/User.vala +++ b/src/API/Misskey/User.vala @@ -1,4 +1,4 @@ -public class Tuba.API.Misskey.User : Entity, AiChanify, Json.Serializable { +public class Tuba.API.Misskey.User : Entity, Widgetizable, AiChanify, Json.Serializable { public string id { get; set; } public string name { get; set; } public string username { get; set; } @@ -33,4 +33,8 @@ public class Tuba.API.Misskey.User : Entity, AiChanify, Json.Serializable { return masto_acc; } + + public override Gtk.Widget to_widget () { + return to_mastodon ().to_widget (); + } } From a7187d0efe0e46e10b615e2369c7c62fa067aa6a Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 00:35:57 +0300 Subject: [PATCH 10/14] fix(Note): renote can be both quote and reblog --- src/API/Misskey/Note.vala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index b94cadfb8..9b1524d8a 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -11,6 +11,7 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, Ai public string? replyId { get; set; default = null; } public string visibility { get; set; default = settings.default_post_visibility; } public Gee.ArrayList? mentions { get; set; default = null; } + public Gee.ArrayList? fileIds { get; set; default = null; } // public API.Poll? poll { get; set; default = null; } public Gee.ArrayList? emojis { get; set; } public Gee.HashMap? reactions { get; set; default = null; } @@ -70,7 +71,12 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, Ai if (renote != null) { var reblog_status = renote.to_mastodon () as API.Status; - masto_status.reblog = reblog_status; + + if (text == null && (fileIds == null || fileIds.size == 0)) { + masto_status.reblog = reblog_status; + } else { + masto_status.quote = reblog_status; + } } return masto_status; From c6c17870977afde530f3da4c801f028b89d5eed8 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 00:40:07 +0300 Subject: [PATCH 11/14] chore: lint --- src/API/Entity.vala | 4 ++-- src/API/Misskey.vala | 2 +- src/API/Misskey/Note.vala | 8 ++++---- src/Dialogs/NewAccount.vala | 2 +- src/Services/Accounts/SecretAccountStore.vala | 2 +- src/Views/Timeline.vala | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/API/Entity.vala b/src/API/Entity.vala index 82240c8dd..7cdd990c5 100644 --- a/src/API/Entity.vala +++ b/src/API/Entity.vala @@ -181,7 +181,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { public static bool des_map_string_string (out Value val, Json.Node node) { var map = new Gee.HashMap (); if (!node.is_null ()) { - node.get_object ().foreach_member((obj, t_key, t_node) => { + node.get_object ().foreach_member ((obj, t_key, t_node) => { map.set (t_key, (string) t_node.get_string ()); }); } @@ -192,7 +192,7 @@ public class Tuba.Entity : GLib.Object, Widgetizable, Json.Serializable { public static bool des_map (out Value val, Json.Node node, Type type) { var map = new Gee.HashMap (); if (!node.is_null ()) { - node.get_object ().foreach_member((obj, t_key, t_node) => { + node.get_object ().foreach_member ((obj, t_key, t_node) => { try { var t_obj = Entity.from_json (type, t_node); map.set (t_key, t_obj); diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index 83390321f..8bcb6f370 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -120,7 +120,7 @@ public class Tuba.API.Misskey.Utils : Object { public static string generate_i (string secret, string access_token) { string pre_c = @"$access_token$secret"; Checksum checksum = new Checksum (ChecksumType.SHA256); - + checksum.update (pre_c.data, -1); return checksum.get_string (); } diff --git a/src/API/Misskey/Note.vala b/src/API/Misskey/Note.vala index 9b1524d8a..4e5a6cb65 100644 --- a/src/API/Misskey/Note.vala +++ b/src/API/Misskey/Note.vala @@ -1,6 +1,6 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, AiChanify { ~Note () { - message ("[OBJ] Destroyed "+uri); + message (@"[OBJ] Destroyed $uri"); } public string id { get; set; } @@ -25,7 +25,7 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, Ai return Entity.from_json (typeof (API.Misskey.Note), node) as API.Misskey.Note; } - public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) { + public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) { var success = default_deserialize_property (prop, out val, spec, node); var type = spec.value_type; @@ -40,7 +40,7 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, Ai switch (prop) { case "mentions": - return Entity.des_list_string(out val, node); + return Entity.des_list_string (out val, node); case "emojis": contains = typeof (API.Misskey.Emoji); break; @@ -68,7 +68,7 @@ public class Tuba.API.Misskey.Note : Entity, Widgetizable, Json.Serializable, Ai masto_status.visibility = visibility; masto_status.uri = uri; masto_status.created_at = createdAt; - + if (renote != null) { var reblog_status = renote.to_mastodon () as API.Status; diff --git a/src/Dialogs/NewAccount.vala b/src/Dialogs/NewAccount.vala index 111e1b5fb..4195d0a8a 100644 --- a/src/Dialogs/NewAccount.vala +++ b/src/Dialogs/NewAccount.vala @@ -153,7 +153,7 @@ public class Tuba.Dialogs.NewAccount: Adw.Window { .body_json (Tuba.API.Misskey.JSON.get_session_generate (account.client_secret)); yield mk_msg.await (); - parser = Network.get_parser_from_inputstream(mk_msg.response_body); + parser = Network.get_parser_from_inputstream (mk_msg.response_body); root = network.parse (parser); var callbackurl = root.get_string_member ("url"); warning (account.client_id); diff --git a/src/Services/Accounts/SecretAccountStore.vala b/src/Services/Accounts/SecretAccountStore.vala index d4aac3460..0de09e8cb 100644 --- a/src/Services/Accounts/SecretAccountStore.vala +++ b/src/Services/Accounts/SecretAccountStore.vala @@ -133,7 +133,7 @@ public class Tuba.SecretAccountStore : AccountStore { account.instance_emojis = null; account.t_connection_url = null; account.source = null; - + generator.set_root (account.to_json ()); var secret = generator.to_data (null); // translators: The variable is the backend like "Mastodon" diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index dae98f1ba..f106f77d7 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -195,14 +195,14 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase Object[] to_add = {}; Network.parse_array (msg, parser, node => { var e = entity_cache.lookup_or_insert (node, accepts); - + var e_aichanified = e as API.Misskey.AiChanify; if (e_aichanified != null) { to_add += (Entity) e_aichanified.to_mastodon (); } }); model.splice (model.get_n_items (), 0, to_add); - + // get_pages (msg.response_headers.get_one ("Link")); on_content_changed (); on_request_finish (); From 6e200234aaa9f34387ad16db83d42230c13b42da Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 00:40:39 +0300 Subject: [PATCH 12/14] feat(lint): disable naming conventions mk uses camelcase --- vala-lint.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/vala-lint.conf b/vala-lint.conf index a8e60925c..7cda50b38 100644 --- a/vala-lint.conf +++ b/vala-lint.conf @@ -1,2 +1,3 @@ [Checks] use-of-tabs=off +naming-convention=off From c2755d032dc3798ce6c993e092eb39be08ea0999 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 15:35:47 +0300 Subject: [PATCH 13/14] feat(timelines): favorites --- src/API/Misskey.vala | 7 ++++++- src/API/Misskey/Favorite.vala | 12 ++++++++++++ src/API/Misskey/meson.build | 1 + src/Views/Favorites.vala | 6 ++++-- src/Views/Timeline.vala | 3 ++- 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 src/API/Misskey/Favorite.vala diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index 8bcb6f370..7943bedda 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -85,7 +85,7 @@ public class Tuba.API.Misskey.JSON : Object { return builder; } - public static Json.Builder get_timeline (int limit = 20) { + public static Json.Builder get_timeline (int limit = 20, string? userId = null) { var builder = new Json.Builder (); builder.begin_object (); @@ -95,6 +95,11 @@ public class Tuba.API.Misskey.JSON : Object { builder.set_member_name ("limit"); builder.add_int_value (limit); + if (userId != null) { + builder.set_member_name ("userId"); + builder.add_string_value (userId); + } + builder.end_object (); return builder; diff --git a/src/API/Misskey/Favorite.vala b/src/API/Misskey/Favorite.vala new file mode 100644 index 000000000..4e089dac6 --- /dev/null +++ b/src/API/Misskey/Favorite.vala @@ -0,0 +1,12 @@ +public class Tuba.API.Misskey.Favorite : Entity, Widgetizable, AiChanify { + public string kind { get; set; } + public API.Misskey.Note note { get; set; } + + public override Entity to_mastodon () { + return note.to_mastodon (); + } + + public override Gtk.Widget to_widget () { + return to_mastodon ().to_widget (); + } +} diff --git a/src/API/Misskey/meson.build b/src/API/Misskey/meson.build index face1b883..f3ca8bc0b 100644 --- a/src/API/Misskey/meson.build +++ b/src/API/Misskey/meson.build @@ -1,6 +1,7 @@ sources += files( 'AiChanify.vala', 'Bookmark.vala', + 'Favorite.vala', 'Emoji.vala', 'Note.vala', 'User.vala', diff --git a/src/Views/Favorites.vala b/src/Views/Favorites.vala index 97c0206de..2f7704c66 100644 --- a/src/Views/Favorites.vala +++ b/src/Views/Favorites.vala @@ -2,9 +2,11 @@ public class Tuba.Views.Favorites : Views.Timeline { public Favorites () { Object ( - url: "/api/v1/favourites", - label: _("Favorites") + url: "/api/users/reactions", + label: _("Favorites"), + with_user_id: true ); + accepts = typeof (API.Misskey.Favorite); } } diff --git a/src/Views/Timeline.vala b/src/Views/Timeline.vala index f106f77d7..5d8df6e9e 100644 --- a/src/Views/Timeline.vala +++ b/src/Views/Timeline.vala @@ -7,6 +7,7 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase public bool is_public { get; construct set; default = false; } public Type accepts { get; set; default = typeof (API.Status); } public bool use_queue { get; set; default = true; } + public bool with_user_id { get; set; default=false; } protected InstanceAccount? account { get; set; default = null; } @@ -188,7 +189,7 @@ public class Tuba.Views.Timeline : AccountHolder, Streamable, Views.ContentBase new Request.POST (get_req_url ()) .with_account (account) .with_ctx (this) - .body_json (Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size)) + .body_json (Tuba.API.Misskey.JSON.get_timeline (settings.timeline_page_size, with_user_id ? account.id : null)) .then ((sess, msg, in_stream) => { var parser = Network.get_parser_from_inputstream (in_stream); From 4824df2014e90f0109dbc0f61ff19a8cda3934d0 Mon Sep 17 00:00:00 2001 From: Evangelos Paterakis Date: Fri, 7 Jul 2023 16:00:01 +0300 Subject: [PATCH 14/14] feat(status): delete --- src/API/Misskey.vala | 12 ++++++++++++ src/API/Status.vala | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/API/Misskey.vala b/src/API/Misskey.vala index 7943bedda..972ecada5 100644 --- a/src/API/Misskey.vala +++ b/src/API/Misskey.vala @@ -119,6 +119,18 @@ public class Tuba.API.Misskey.JSON : Object { return builder; } + + public static Json.Builder get_delete_note (string noteId) { + var builder = new Json.Builder (); + builder.begin_object (); + + builder.set_member_name ("noteId"); + builder.add_string_value (noteId); + + builder.end_object (); + + return builder; + } } public class Tuba.API.Misskey.Utils : Object { diff --git a/src/API/Status.vala b/src/API/Status.vala index 330caba76..867526caf 100644 --- a/src/API/Status.vala +++ b/src/API/Status.vala @@ -193,7 +193,10 @@ public class Tuba.API.Status : Entity, Widgetizable { } public Request annihilate () { - return new Request.DELETE (@"/api/v1/statuses/$id") - .with_account (accounts.active); + // return new Request.DELETE (@"/api/v1/statuses/$id") + // .with_account (accounts.active); + return new Request.POST ("/api/notes/delete") + .with_account (accounts.active) + .body_json (API.Misskey.JSON.get_delete_note (id)); } }