2020-09-10 17:10:24 +00:00
using Gtk ;
2023-02-24 04:15:06 +00:00
[ GtkTemplate ( ui = " /dev/geopjr/Tooth/ui/dialogs/new_account.ui " ) ]
2022-11-13 20:57:43 +00:00
public class Tooth . Dialogs . NewAccount : Adw . Window {
2020-09-10 17:10:24 +00:00
2023-01-10 16:52:40 +00:00
const string AUTO_AUTH_DESCRIPTION = _ ( " Allow access to your account in the browser. If something went wrong, <a href= \" tooth://manual_auth \" >try manual authorization</a>. " ) ;
2023-02-24 04:19:16 +00:00
const string CODE_AUTH_DESCRIPTION = _ ( " Copy the authorization code from the browser and paste it below. " ) ;
2023-01-10 16:52:40 +00:00
2021-02-14 11:49:41 +00:00
const string scopes = " read write follow " ;
2020-09-10 17:10:24 +00:00
protected bool is_working { get ; set ; default = false ; }
protected string ? redirect_uri { get ; set ; }
protected bool use_auto_auth { get ; set ; default = true ; }
protected InstanceAccount account { get ; set ; default = new InstanceAccount . empty ( " " ) ; }
2021-07-23 11:41:03 +00:00
[ GtkChild ] unowned Adw . Leaflet deck ;
[ GtkChild ] unowned Box instance_step ;
[ GtkChild ] unowned Box code_step ;
[ GtkChild ] unowned Box done_step ;
2023-01-10 16:52:40 +00:00
[ GtkChild ] unowned Adw . EntryRow instance_entry ;
2022-11-16 23:18:24 +00:00
[ GtkChild ] unowned Label instance_entry_error ;
2023-01-10 16:52:40 +00:00
[ GtkChild ] unowned Adw . EntryRow code_entry ;
2022-11-16 23:18:24 +00:00
[ GtkChild ] unowned Label code_entry_error ;
2023-01-10 16:52:40 +00:00
[ GtkChild ] unowned Adw . StatusPage auth_page ;
2021-07-23 11:41:03 +00:00
[ GtkChild ] unowned Adw . StatusPage done_page ;
2020-09-10 17:10:24 +00:00
public NewAccount ( ) {
2021-07-23 11:41:03 +00:00
Object ( transient_for : app . main_window ) ;
app . add_account_window = this ;
app . add_window ( this ) ;
2020-09-10 17:10:24 +00:00
2023-01-10 16:52:40 +00:00
bind_property ( " use-auto-auth " , auth_page , " description " , BindingFlags . SYNC_CREATE , ( b , src , ref target ) = > {
target . set_string ( src . get_boolean ( ) ? AUTO_AUTH_DESCRIPTION : CODE_AUTH_DESCRIPTION ) ;
return true ;
} ) ;
2022-11-16 23:18:24 +00:00
2023-01-10 16:52:40 +00:00
reset ( ) ;
present ( ) ;
2020-09-10 17:10:24 +00:00
}
2021-07-23 11:41:03 +00:00
public override bool close_request ( ) {
2022-11-16 21:21:43 +00:00
warning ( " Close Request " ) ;
2021-07-23 11:41:03 +00:00
app . add_account_window = null ;
return base . close_request ( ) ;
2020-09-10 17:10:24 +00:00
}
string setup_redirect_uri ( ) {
try {
if ( ! use_auto_auth )
throw new Oopsie . INTERNAL ( " Using manual auth method " ) ;
2022-11-16 23:17:42 +00:00
// GLib.Process.spawn_command_line_sync (@"xdg-mime default $(Build.DOMAIN).desktop x-scheme-handler/tooth");
2020-09-10 17:10:24 +00:00
2022-11-16 23:17:42 +00:00
// message ("Successfully associated MIME type for automatic authorization");
2022-11-13 20:57:43 +00:00
return " tooth://auth_code " ;
2020-09-10 17:10:24 +00:00
}
catch ( Error e ) {
warning ( e . message ) ;
use_auto_auth = false ;
return " urn:ietf:wg:oauth:2.0:oob " ;
}
}
void reset ( ) {
message ( " Reset state " ) ;
2022-11-16 23:18:24 +00:00
clear_errors ( ) ;
2022-11-16 23:17:42 +00:00
use_auto_auth = true ;
2020-09-10 17:10:24 +00:00
account = new InstanceAccount . empty ( account . instance ) ;
2021-07-23 11:41:03 +00:00
deck . visible_child = instance_step ;
2020-09-10 17:10:24 +00:00
}
void oopsie ( string title , string msg = " " ) {
2022-11-16 23:17:42 +00:00
warning ( @" $title $msg. " ) ;
2020-09-10 17:10:24 +00:00
app . inform ( Gtk . MessageType . ERROR , title , msg , this ) ;
}
async void step ( ) throws Error {
2021-07-23 11:41:03 +00:00
if ( deck . visible_child = = instance_step ) {
2020-09-10 17:10:24 +00:00
setup_instance ( ) ;
2021-02-12 15:26:37 +00:00
yield accounts . guess_backend ( account ) ;
}
2020-09-10 17:10:24 +00:00
if ( account . client_secret = = null | | account . client_id = = null ) {
yield register_client ( ) ;
return ;
}
yield request_token ( ) ;
}
void setup_instance ( ) throws Error {
2021-07-23 11:41:03 +00:00
message ( " Checking instance URL " ) ;
2020-09-10 17:10:24 +00:00
var str = instance_entry . text
. replace ( " / " , " " )
. replace ( " : " , " " )
. replace ( " https " , " " )
. replace ( " http " , " " ) ;
account . instance = " https:// " + str ;
instance_entry . text = str ;
if ( str . char_count ( ) < = 0 | | ! ( " . " in account . instance ) )
throw new Oopsie . USER ( _ ( " Please enter a valid instance URL " ) ) ;
}
async void register_client ( ) throws Error {
message ( " Registering client " ) ;
var msg = new Request . POST ( @" /api/v1/apps " )
. with_account ( account )
2021-02-14 11:49:41 +00:00
. with_form_data ( " client_name " , Build . NAME )
2021-07-23 11:41:03 +00:00
. with_form_data ( " redirect_uris " , redirect_uri = setup_redirect_uri ( ) )
2021-02-14 11:49:41 +00:00
. with_form_data ( " scopes " , scopes )
2021-07-23 11:41:03 +00:00
. with_form_data ( " website " , Build . WEBSITE ) ;
2020-09-10 17:10:24 +00:00
yield msg . await ( ) ;
var root = network . parse ( msg ) ;
account . client_id = root . get_string_member ( " client_id " ) ;
account . client_secret = root . get_string_member ( " client_secret " ) ;
message ( " OK: Instance registered client " ) ;
2021-07-23 11:41:03 +00:00
deck . visible_child = code_step ;
2020-09-10 17:10:24 +00:00
open_confirmation_page ( ) ;
}
void open_confirmation_page ( ) {
message ( " Opening permission request page " ) ;
var pars = @" scope=$scopes&response_type=code&redirect_uri=$redirect_uri&client_id=$(account.client_id) " ;
var url = @" $(account.instance)/oauth/authorize?$pars " ;
2021-07-23 11:41:03 +00:00
Host . open_uri ( url ) ;
2020-09-10 17:10:24 +00:00
}
async void request_token ( ) throws Error {
2021-07-23 11:41:03 +00:00
if ( code_entry . text . char_count ( ) < = 10 )
2020-09-10 17:10:24 +00:00
throw new Oopsie . USER ( _ ( " Please enter a valid authorization code " ) ) ;
message ( " Requesting access 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 root = network . parse ( token_req ) ;
account . access_token = root . get_string_member ( " access_token " ) ;
if ( account . access_token = = null )
throw new Oopsie . INSTANCE ( _ ( " Instance failed to authorize the access token " ) ) ;
2021-02-12 15:26:37 +00:00
yield account . verify_credentials ( ) ;
2020-09-10 17:10:24 +00:00
2021-02-12 15:26:37 +00:00
account = accounts . create_account ( account . to_json ( ) ) ;
2020-09-10 17:10:24 +00:00
message ( " Saving account " ) ;
accounts . add ( account ) ;
2023-01-10 16:52:40 +00:00
done_page . title = _ ( " Hello, %s! " ) . printf ( account . display_name ) ;
2021-07-23 11:41:03 +00:00
deck . visible_child = done_step ;
2021-02-12 15:26:37 +00:00
message ( " Switching to account " ) ;
accounts . activate ( account ) ;
2020-09-10 17:10:24 +00:00
}
public void redirect ( string uri ) {
present ( ) ;
message ( @" Received uri: $uri " ) ;
2023-01-10 16:52:40 +00:00
if ( " manual_auth " in uri ) {
use_auto_auth = false ;
register_client . begin ( ) ;
// reset();
return ;
}
2020-09-10 17:10:24 +00:00
var query = new Soup . URI ( uri ) . get_query ( ) ;
var split = query . split ( " = " ) ;
var code = split [ 1 ] ;
2023-02-12 21:13:56 +00:00
var code_clean = code . splice ( code . index_of_char ( '&' ) , - 1 ) ;
2020-09-10 17:10:24 +00:00
2023-02-12 21:13:56 +00:00
code_entry . text = code_clean ;
2020-09-10 17:10:24 +00:00
is_working = false ;
on_next_clicked ( ) ;
}
2022-11-16 23:18:24 +00:00
public void mark_errors ( string error_message ) {
instance_entry . add_css_class ( " error " ) ;
instance_entry_error . label = error_message ;
code_entry . add_css_class ( " error " ) ;
code_entry_error . label = error_message ;
}
2023-01-10 16:52:40 +00:00
[ GtkCallback ]
2022-11-16 23:18:24 +00:00
public void clear_errors ( ) {
instance_entry . remove_css_class ( " error " ) ;
instance_entry_error . label = " " ;
code_entry . remove_css_class ( " error " ) ;
code_entry_error . label = " " ;
}
2020-09-10 17:10:24 +00:00
[ GtkCallback ]
void on_next_clicked ( ) {
2022-11-16 23:18:24 +00:00
clear_errors ( ) ;
2020-09-10 17:10:24 +00:00
if ( is_working ) return ;
is_working = true ;
step . begin ( ( obj , res ) = > {
try {
step . end ( res ) ;
2022-11-16 23:18:24 +00:00
clear_errors ( ) ;
2020-09-10 17:10:24 +00:00
}
catch ( Oopsie . INSTANCE e ) {
oopsie ( _ ( " Server returned an error " ) , e . message ) ;
2022-11-16 23:18:24 +00:00
mark_errors ( e . message ) ;
2020-09-10 17:10:24 +00:00
}
catch ( Error e ) {
oopsie ( e . message ) ;
2022-11-16 23:18:24 +00:00
mark_errors ( e . message ) ;
2020-09-10 17:10:24 +00:00
}
is_working = false ;
} ) ;
}
2022-11-16 23:17:42 +00:00
[ GtkCallback ]
void on_done_clicked ( ) {
app . present_window ( ) ;
app . add_account_window = null ;
destroy ( ) ;
}
2020-09-10 17:10:24 +00:00
[ GtkCallback ]
void on_back_clicked ( ) {
reset ( ) ;
}
2021-07-23 11:41:03 +00:00
[ GtkCallback ]
void on_visible_child_notify ( ) {
if ( ! deck . child_transition_running & & deck . visible_child = = instance_step )
reset ( ) ;
2022-11-13 09:13:52 +00:00
deck . can_navigate_back = deck . visible_child ! = done_step ;
2021-07-23 11:41:03 +00:00
}
2020-09-10 17:10:24 +00:00
}
2021-02-12 15:26:37 +00:00