Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/Dialogs/CloneRepositoryDialog.vala
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ public class Scratch.Dialogs.CloneRepositoryDialog : Granite.MessageDialog {

public CloneRepositoryDialog (string _suggested_local_folder) {
Object (
transient_for: ((Gtk.Application)(GLib.Application.get_default ())).get_active_window (),
image_icon: new ThemedIcon ("git"),
modal: true,
suggested_local_folder: _suggested_local_folder
);
}

construct {
transient_for = ((Gtk.Application)(GLib.Application.get_default ())).get_active_window ();
image_icon = new ThemedIcon ("git");
modal = true;

try {
name_regex = new Regex (NAME_REGEX, OPTIMIZE, ANCHORED | NOTEMPTY);
} catch (RegexError e) {
Expand Down
43 changes: 43 additions & 0 deletions src/Dialogs/CloningProgressDialog.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: 2025 elementary, Inc. <https://elementary.io>
*
* Authored by: Jeremy Wootten <[email protected]>
*/

public class Scratch.Dialogs.CloningProgressDialog : Granite.MessageDialog {

public string remote_uri { get; construct; }
public string local_folder_path { get; construct; }

public CloningProgressDialog (MainWindow parent, string remote_uri, string local_folder_path) {
Object (
transient_for: parent,
buttons: Gtk.ButtonsType.NONE,
remote_uri: remote_uri,
local_folder_path: local_folder_path
);
}

construct {
image_icon = new ThemedIcon ("git");
primary_text = _("Cloning a remote repository is in progress");
secondary_text = _("Source: '%s' Cloning to: '%s'").printf (remote_uri, local_folder_path);
}

public void update_status (Scratch.Services.CloningStatus status, string? message = null) {
switch (status) {
case START:
return;
case END_SUCCESS:
primary_text = _("Cloning succeeded");
break;
case END_FAIL:
primary_text = _("Cloning failed");
break;
}

secondary_text = message;
add_button (_("Ok"), Gtk.ResponseType.ACCEPT);
}
}
82 changes: 50 additions & 32 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ namespace Scratch {

document_manager = Scratch.Services.DocumentManager.get_instance ();
git_manager = Services.GitManager.get_instance ();
git_manager.cloning_status.connect (on_cloning_status_changed);

actions = new SimpleActionGroup ();
actions.add_action_entries (ACTION_ENTRIES, this);
Expand Down Expand Up @@ -1043,44 +1044,61 @@ namespace Scratch {
}

private void action_clone_repo (SimpleAction action, Variant? param) {
var uri = "";
//By default, create clone in parent of the current project
var local_folder = Path.get_dirname (git_manager.active_project_path);
var local_name = "";
var clone_dialog = new Dialogs.CloneRepositoryDialog (local_folder);
clone_dialog.response.connect ((res) => {
if (res == Gtk.ResponseType.APPLY) {
uri = clone_dialog.get_source_repository_uri ();
local_folder = clone_dialog.get_local_folder ();
local_name = clone_dialog.get_local_name ();
}
if (cloning_progress_dialog != null) {
critical ("Cloning attempt while cloning in progress");
// We do not support simultaneous cloning at the moment.
return;
}

var default_folder = git_manager.active_project_path != "" ?
Path.get_dirname (git_manager.active_project_path) : "";
var clone_dialog = new Dialogs.CloneRepositoryDialog (default_folder);
clone_dialog.response.connect ((res) => {
var uri = clone_dialog.get_source_repository_uri ();
var local_folder = clone_dialog.get_local_folder ();
var local_name = clone_dialog.get_local_name ();
var can_clone = clone_dialog.can_clone;
// MainWindow should provide feedback on cloning progress - close modal dialog now
clone_dialog.destroy ();
if (res == Gtk.ResponseType.APPLY && can_clone) { // Should not need second test?
//TODO Show progress while cloning
cloning_progress_dialog = new Scratch.Dialogs.CloningProgressDialog (this, uri, local_folder);
cloning_progress_dialog.response.connect ((res) => {
cloning_progress_dialog.destroy ();
cloning_progress_dialog = null;
});
cloning_progress_dialog.present ();

//Need tiimout in order for dialog to be drawn else cloning blocks it.
//TODO Find a more elegant way
Timeout.add (500, () => {
git_manager.clone_repository.begin (
uri,
Path.build_filename (Path.DIR_SEPARATOR_S, local_folder, local_name),
(obj, res) => {
File? workdir = null;
if (git_manager.clone_repository.end (res, out workdir)) {
debug ("Repository cloned into %s", workdir.get_uri ());
//TODO Optionally open folder from progress dialog?
open_folder (workdir);
}
}
);
return Source.REMOVE;
});
}
});

clone_dialog.present ();
}

if (clone_dialog.can_clone) {
//TODO Show progress while cloning
git_manager.clone_repository.begin (
uri,
Path.build_filename (Path.DIR_SEPARATOR_S, local_folder, local_name),
(obj, res) => {
try {
File? workdir = null;
if (git_manager.clone_repository.end (res, out workdir)) {
debug ("Repository cloned into %s", workdir.get_uri ());
open_folder (workdir);
//TODO Make active according to dialog checkbox
}
} catch (Error e) {
warning ("Unable to clone '%s'. %s", uri, e.message);
}
}
);
} else {
//TODO Give feedback
}
private Scratch.Dialogs.CloningProgressDialog? cloning_progress_dialog;
private void on_cloning_status_changed (
Scratch.Services.CloningStatus status,
string? message = null
) requires (cloning_progress_dialog != null) {

cloning_progress_dialog.update_status (status, message);
}

private void action_collapse_all_folders () {
Expand Down
48 changes: 37 additions & 11 deletions src/Services/GitManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@
*/

namespace Scratch.Services {
public enum CloningStatus {
START,
END_SUCCESS,
END_FAIL
}

public class GitManager : Object {
public signal void cloning_status (CloningStatus cloning_status, string? message = null);
public ListStore project_liststore { get; private set; }
public string active_project_path { get; set; default = "";}

Expand Down Expand Up @@ -117,9 +124,7 @@ namespace Scratch.Services {
string uri,
string local_folder,
out File? repo_workdir
) throws Error {

repo_workdir = null;
) {
var folder_file = File.new_for_path (local_folder);

var fetch_options = new Ggit.FetchOptions ();
Expand All @@ -131,17 +136,38 @@ namespace Scratch.Services {
clone_options.set_is_bare (false);
clone_options.set_fetch_options (fetch_options);

var new_repo = Ggit.Repository.clone (
uri,
folder_file,
clone_options
);
cloning_status (START);
// Gtk.main_iteration ();
var success = false;
File? workdir = null;
Idle.add (() => {
try {
var new_repo = Ggit.Repository.clone (
uri,
folder_file,
clone_options
);

if (new_repo != null) {
workdir = new_repo.get_workdir ();
}
} catch (Error e) {
warning ("Error cloning %s", e.message);
cloning_status (END_FAIL, e.message);
}

clone_repository.callback ();
return Source.REMOVE;
});

yield; //Clone still blocks main thread

if (new_repo != null) {
repo_workdir = new_repo.get_workdir ();
if (workdir != null) {
cloning_status (END_SUCCESS, _("The local repository working directory is '%s'").printf (workdir.get_uri ()));
}

return new_repo != null;
repo_workdir = workdir;
return workdir != null;
}
}
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ code_files = files(
'Dialogs/RestoreConfirmationDialog.vala',
'Dialogs/CloseProjectsConfirmationDialog.vala',
'Dialogs/CloneRepositoryDialog.vala',
'Dialogs/CloningProgressDialog.vala',
'Dialogs/OverwriteUncommittedConfirmationDialog.vala',
'Dialogs/GlobalSearchDialog.vala',
'Dialogs/NewBranchDialog.vala',
Expand Down