Tooth/src/Views/NewAccount.vala

187 lines
5.1 KiB
Vala

using Gtk;
public class Tootle.Views.NewAccount : Views.Base {
private string? instance { get; set; }
private string? code { get; set; }
private string scopes = "read%20write%20follow";
private string? client_id { get; set; }
private string? client_secret { get; set; }
private string? access_token { get; set; }
private string redirect_uri { get; set; default = "urn:ietf:wg:oauth:2.0:oob"; } //TODO: Investigate URI handling for automatic token getting
private InstanceAccount account;
private Button next_button;
private Entry instance_entry;
private Entry code_entry;
private Label reset_label;
private Stack stack;
private Widget step1;
private Widget step2;
public NewAccount (bool allow_closing = true) {
Object (
allow_closing: allow_closing,
label: _("New Account")
);
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/views/new_account.ui");
content.pack_start (builder.get_object ("wizard") as Grid);
state = "content";
next_button = builder.get_object ("next") as Button;
reset_label = builder.get_object ("reset") as Label;
instance_entry = builder.get_object ("instance_entry") as Entry;
code_entry = builder.get_object ("code_entry") as Entry;
stack = builder.get_object ("stack") as Stack;
step1 = builder.get_object ("step1") as Widget;
step2 = builder.get_object ("step2") as Widget;
next_button.clicked.connect (on_next_clicked);
reset_label.activate_link.connect (reset);
instance_entry.text = "https://mastodon.social/"; //TODO: REMOVE ME
info ("New account view was requested");
}
bool reset () {
info ("State invalidated");
instance = code = client_id = client_secret = access_token = null;
instance_entry.sensitive = true;
stack.visible_child = step1;
return true;
}
void oopsie (string message) {
warning (message);
}
void on_next_clicked () {
try {
step ();
}
catch (Oopsie e) {
oopsie (e.message);
}
}
void step () throws Error {
if (instance == null)
setup_instance ();
if (client_secret == null || client_id == null) {
register_client ();
return;
}
code = code_entry.text;
request_token ();
}
void setup_instance () throws Error {
info ("Checking instance URL");
var str = instance_entry.text
.replace ("/", "")
.replace (":", "")
.replace ("https", "")
.replace ("http", "");
instance = "https://"+str;
instance_entry.text = str;
if (str.char_count () <= 0 || !("." in instance))
throw new Oopsie.USER (_("Instance URL is invalid"));
}
void register_client () throws Error {
info ("Registering client");
instance_entry.sensitive = false;
account = new InstanceAccount.empty (instance);
new Request.POST (@"/api/v1/apps")
.with_param ("client_name", Build.NAME)
.with_param ("website", Build.WEBSITE)
.with_param ("scopes", scopes)
.with_param ("redirect_uris", redirect_uri)
.with_account (account)
.then ((sess, msg) => {
var root = network.parse (msg);
client_id = root.get_string_member ("client_id");
client_secret = root.get_string_member ("client_secret");
info ("OK: instance registered client");
stack.visible_child = step2;
open_confirmation_page ();
})
.on_error ((status, reason) => {
oopsie (reason);
instance_entry.sensitive = true;
})
.exec ();
}
void open_confirmation_page () {
info ("Opening permission request page");
var pars = @"scope=$scopes&response_type=code&redirect_uri=$redirect_uri&client_id=$client_id";
var url = @"$instance/oauth/authorize?$pars";
Desktop.open_uri (url);
}
void request_token () throws Error {
if (code.char_count () <= 10)
throw new Oopsie.USER (_("Please paste a valid authorization code"));
info ("Requesting access token");
new Request.POST (@"/oauth/token")
.with_account (account)
.with_param ("client_id", client_id)
.with_param ("client_secret", client_secret)
.with_param ("redirect_uri", redirect_uri)
.with_param ("grant_type", "authorization_code")
.with_param ("code", code)
.then ((sess, msg) => {
var root = network.parse (msg);
access_token = root.get_string_member ("access_token");
account.access_token = access_token;
account.id = "";
info ("OK: received access token");
request_profile ();
})
.on_error ((code, reason) => oopsie (reason))
.exec ();
}
void request_profile () throws Error {
info ("Testing received access token");
new Request.GET ("/api/v1/accounts/verify_credentials")
.with_account (account)
.then ((sess, msg) => {
var node = network.parse_node (msg);
var account = API.Account.from (node);
info ("OK: received user profile");
save (account);
})
.on_error ((status, reason) => {
reset ();
oopsie (reason);
})
.exec ();
}
void save (API.Account profile) {
info ("Account validated. Saving...");
account.patch (profile);
account.instance = instance;
account.client_id = client_id;
account.client_secret = client_secret;
account.access_token = access_token;
accounts.add (account);
destroy ();
}
}