| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- using Gtk;
- using Adw;
- using Invercargill;
- namespace Publicate {
- public class PublishWindow : Adw.Window {
- private Adw.HeaderBar header_bar;
- private ViewerWindow window;
- private Stack stack;
- private Box loader;
- private Label loader_status;
- private ProgressBar loader_progress;
- private bool loader_pulsing = false;
- private Box identity_select;
- private Label identity_header;
- private IdentityList identity_list;
- private Button publish_button;
- private Box decision;
- private Label decision_heading;
- private Label decision_body;
- private Button cancel_button;
- private Button continue_button;
- private Box error_view;
- private Label error_heading;
- private Label error_body;
- private Box complete;
- private StatusPage status_page;
- private delegate void DecisionCallback();
- private DecisionCallback cancel_action;
- private DecisionCallback continue_action;
- public PublishWindow(ViewerWindow window) {
- this.window = window;
- modal = true;
- transient_for = window;
- var box = new Box(Orientation.VERTICAL, 0);
- header_bar = new Adw.HeaderBar();
- header_bar.show_end_title_buttons = false;
- title = "Publish";
- default_height = 500;
- default_width = 700;
- box.append (header_bar);
- stack = new Stack();
- stack.vexpand = true;
- stack.transition_type = StackTransitionType.CROSSFADE;
- box.append(stack);
- content = box;
- loader = new Box(Orientation.VERTICAL, 8);
- loader_status = new Label ("Please wait…");
- loader_status.halign = Align.START;
- loader_progress = new ProgressBar ();
- loader_progress.hexpand = true;
- loader.append (loader_status);
- loader.append (loader_progress);
- loader.width_request = 550;
- loader.valign = Align.CENTER;
- loader.halign = Align.CENTER;
-
- stack.add_child (loader);
- stack.visible_child = loader;
- identity_select = new Box(Orientation.VERTICAL, 8);
- identity_select.margin_top = 18;
- identity_select.margin_start = 18;
- identity_select.margin_end = 18;
- identity_select.margin_bottom = 18;
- identity_header = new Label("Select identity to publish with");
- identity_header.halign = Align.START;
- identity_header.add_css_class("title-2");
- identity_select.append(identity_header);
- identity_list = new IdentityList(window);
- identity_select.append(identity_list);
-
- publish_button = new Button.with_label("Publish");
- publish_button.add_css_class("suggested-action");
- publish_button.halign = Align.END;
- publish_button.clicked.connect(() => continue_action());
- identity_select.append(publish_button);
-
- stack.add_child(identity_select);
- error_view = new Box(Orientation.VERTICAL, 8);
- error_view.halign = Align.CENTER;
- error_view.valign = Align.CENTER;
- error_view.margin_top = 18;
- error_view.margin_start = 64;
- error_view.margin_end = 64;
- error_view.margin_bottom = 18;
- error_heading.width_request = 200;
- error_heading = new Label("Decision");
- error_heading.halign = Align.START;
- error_heading.add_css_class("title-2");
- error_view.append(error_heading);
- error_body = new Label("Details");
- error_body.halign = Align.START;
- error_body.hexpand = true;
- error_body.wrap = true;
- error_body.wrap_mode = Pango.WrapMode.WORD;
- error_body.natural_wrap_mode = NaturalWrapMode.WORD;
- error_view.append(error_body);
- var error_close_button = new Button.with_label("Close");
- error_close_button.hexpand = true;
- error_close_button.clicked.connect(() => close());
- error_view.append(error_close_button);
- stack.add_child(error_view);
- decision = new Box(Orientation.VERTICAL, 8);
- decision.halign = Align.CENTER;
- decision.valign = Align.CENTER;
- decision.margin_top = 18;
- decision.margin_start = 64;
- decision.margin_end = 64;
- decision.margin_bottom = 18;
- decision.width_request = 200;
- decision_heading = new Label("Decision");
- decision_heading.halign = Align.START;
- decision_heading.add_css_class("title-2");
- decision.append(decision_heading);
- decision_body = new Label("Details");
- decision_body.halign = Align.START;
- decision_body.hexpand = true;
- decision_body.wrap = true;
- decision_body.wrap_mode = Pango.WrapMode.WORD;
- decision_body.natural_wrap_mode = NaturalWrapMode.WORD;
- decision.append(decision_body);
- var button_box = new Box(Orientation.HORIZONTAL, 0);
- button_box.add_css_class("linked");
- cancel_button = new Button.with_label("Cancel");
- cancel_button.hexpand = true;
- cancel_button.clicked.connect(() => cancel_action());
- button_box.append(cancel_button);
- continue_button = new Button.with_label("Continue");
- continue_button.hexpand = true;
- continue_button.clicked.connect(() => continue_action());
- button_box.append(continue_button);
- decision.append(button_box);
- stack.add_child(decision);
- complete = new Box(Orientation.VERTICAL, 8);
- complete.halign = Align.CENTER;
- complete.valign = Align.CENTER;
- complete.margin_top = 18;
- complete.margin_start = 18;
- complete.margin_end = 18;
- complete.margin_bottom = 18;
- status_page = new StatusPage();
- complete.append(status_page);
- status_page.icon_name = "emblem-ok-symbolic";
- status_page.title = "Publish Completed!";
- var close_button = new Button.with_label("Close");
- status_page.child = close_button;
- close_button.width_request = 400;
- close_button.clicked.connect(() => close());
- stack.add_child(complete);
- }
- private void pulse_loader() {
- if(loader_pulsing) {
- return;
- }
- loader_pulsing = true;
- Timeout.add(100, () => {
- if(!loader_pulsing) {
- return false;
- }
- loader_progress.pulse();
- return true;
- });
- }
- private void show_decision(string heading, string body, bool is_destructive, DecisionCallback cancel_cb, DecisionCallback continue_cb) {
- decision_heading.set_text(heading);
- decision_body.set_text(body);
- continue_button.set_css_classes(new string[] { is_destructive ? "destructive-action" : "suggested-action"});
- continue_action = continue_cb;
- cancel_action = cancel_cb;
- stack.visible_child = decision;
- }
- private void show_error(string heading, string body) {
- error_heading.set_text(heading);
- error_body.set_text(body);
- stack.visible_child = error_view;
- error_bell();
- }
- private Ppub.Collection collection;
- private CollectionConfig config;
- private Pprf.Client client;
- private File publication_file;
- private string dest_name;
- private BinaryData cid;
- private bool unpublish = false;
- private DateTime? old_publish_timestamp = null;
- public async void publish_to(CollectionConfig config, File publication_file) {
- title = @"Publish to $(config.name)";
- this.config = config;
- this.publication_file = publication_file;
- dest_name = publication_file.get_basename();
- if(!dest_name.has_suffix(".ppub")) {
- dest_name += ".ppub";
- }
- pulse_loader();
- loader_status.set_text(@"Looking up $(config.domain)…");
- try{
- client = yield window.collection_service.get_client(config);
- }
- catch(Error e) {
- show_error("Server not found", @"Could not find PPRF server details for $(config.domain): $(e.message)");
- return;
- }
- cid = new ByteComposition.from_base64(config.collection_id);
- loader_status.set_text(@"Querying $(config.domain) for collection information…");
- try {
- collection = yield do_in_bg<Ppub.Collection>(() => client.get_collection(cid));
- }
- catch(Error e) {
- show_error("Could not download collection information", @"Failed to download collection information from server: $(e.message)");
- return;
- }
- loader_pulsing = false;
- loader_progress.fraction = 0;
- if(collection.publications.any(p => p.file_name == dest_name)) {
- show_decision("Replace Existing Publication?", @"There is already a published publication with the name \"$dest_name\", if you continue it will be replaced.", true, () => close(), () => select_publish_identity(true));
- }
- else {
- select_publish_identity(false);
- }
- }
- private string unpublish_file;
- public async void unpublish_from(CollectionConfig config, string file) {
- title = @"Unpublish from $(config.name)";
- this.config = config;
- this.unpublish_file = file;
- pulse_loader();
- loader_status.set_text(@"Looking up $(config.domain)…");
- try{
- client = yield window.collection_service.get_client(config);
- }
- catch(Error e) {
- show_error("Server not found", @"Could not find PPRF server details for $(config.domain): $(e.message)");
- return;
- }
- cid = new ByteComposition.from_base64(config.collection_id);
- loader_status.set_text(@"Querying $(config.domain) for collection information…");
- try {
- collection = yield do_in_bg<Ppub.Collection>(() => client.get_collection(cid));
- }
- catch(Error e) {
- show_error("Could not download collection information", @"Failed to download collection information from server: $(e.message)");
- return;
- }
- if(collection.publications.no(p => p.file_name == file)) {
- show_error("Publication not found", @"The publication does not appear in the latest collection data from the server");
- return;
- }
- select_identity("Unpublish", () => complete_unpublish.begin());
- }
- private async void complete_unpublish() {
- header_bar.show_end_title_buttons = false;
- stack.visible_child = loader;
- loader_status.set_text(@"Unpublishing \"$(unpublish_file)\"…");
- pulse_loader();
- try {
- yield do_void_in_bg(() => client.unpublish(cid, unpublish_file, identity_list.selected_identity));
- }
- catch(Error e) {
- show_error("Unpublication failed", @"Failed to unpbulish \"$(unpublish_file)\": $(e.message)");
- return;
- }
- loader_status.set_text(@"Rebuilding index…");
- try {
- yield do_void_in_bg(() => client.rebuild_index(cid, identity_list.selected_identity));
- }
- catch(Error e) {
- show_error("Index rebuild failed", @"The publication has been unpublished from the server, however there was an error rebuilding the collection's index: $(e.message)");
- return;
- }
- status_page.title = "Publication Removed!";
- stack.visible_child = complete;
- }
- private void select_publish_identity(bool unpub) {
- if(unpub) {
- old_publish_timestamp = collection.publications.first_or_default(p => p.file_name == dest_name)?.publication_time;
- }
- unpublish = unpub;
- select_identity("Publish", () => publish_with.begin());
- }
- private void select_identity(string action_label, DecisionCallback continue_cb) {
- continue_action = continue_cb;
- stack.visible_child = identity_select;
- publish_button.label = action_label;
- identity_header.label = @"Select identity to $(action_label.down()) with";
-
- try {
- identity_list.populate_identities(collection);
- }
- catch(Error e) {
- show_error("Error loading identity list", @"There was an error retreiving a list of local identities: $(e.message)");
- return;
- }
- publish_button.sensitive = identity_list.has_entries;
- header_bar.show_end_title_buttons = true;
- }
- private async void publish_with() {
- header_bar.show_end_title_buttons = false;
- stack.visible_child = loader;
- loader_status.set_text(@"Registering name \"$(dest_name)\"…");
- pulse_loader();
- try {
- yield do_void_in_bg(() => client.register_name(cid, dest_name, identity_list.selected_identity));
- }
- catch(Pprf.Messages.PprfFailureError.NAME_EXISTS e) {
- // Ignore if unpublishing anyway
- if(unpublish) {
- yield upload_and_publish(true);
- return;
- }
- else{
- show_decision("Overwrite file?", @"There is already a file on this server with the name \"$dest_name\", if you continue it will be overwritten.", true, () => close(), () => upload_and_publish.begin(true));
- return;
- }
- }
- catch(Error e) {
- show_error("Name registration failed", @"Could not register the name \"$(dest_name)\" with the server: $(e.message)");
- return;
- }
- yield upload_and_publish(false);
- }
- private async void upload_and_publish(bool replace_destination) {
- stack.visible_child = loader;
- loader_status.set_text(@"Preparing to upload publication…");
- pulse_loader();
- try {
- var file_info = yield publication_file.query_info_async("*", FileQueryInfoFlags.NONE, 1);
- var file_size = file_info.get_size();
- var file_stream = yield publication_file.read_async(1);
- var flags = replace_destination ? Pprf.Messages.FinaliseUploadFlags.OVERWRITE_DESTINATION : 0;
- yield do_void_in_bg(() => client.upload(cid, file_stream, file_size, dest_name, unpublish, identity_list.selected_identity, upload_callback, flags));
- }
- catch(Error e) {
- show_error("Upload failed", @"There was an error uploading the publication to the server: $(e.message)");
- return;
- }
-
- pulse_loader();
- loader_status.set_text(@"Computing publication checksum…");
- try {
- var checksum = yield do_in_bg<BinaryData>(() => new ByteComposition.from_byte_array(Pprf.Util.file_checksum(publication_file)));
- loader_status.set_text(@"Signing publication…");
- var timestamp = old_publish_timestamp;
- if(timestamp == null) {
- timestamp = new DateTime.now_local();
- }
- var publication = yield do_in_bg<Ppub.CollectionPublication>(() => new Ppub.CollectionPublication(dest_name, timestamp, identity_list.selected_identity.name, identity_list.selected_credentials, checksum.to_array()));
- loader_status.set_text(@"Publishing…");
- yield do_void_in_bg(() => client.publish(cid, publication, identity_list.selected_identity));
- }
- catch(Error e) {
- show_error("Publication failed", @"There was an error publishing the publication: $(e.message)");
- return;
- }
- loader_status.set_text(@"Rebuilding index…");
- try {
- yield do_void_in_bg(() => client.rebuild_index(cid, identity_list.selected_identity));
- }
- catch(Error e) {
- show_error("Index rebuild failed", @"The publication has been uploaded to the server, however there was an error rebuilding the collection's index: $(e.message)");
- return;
- }
- stack.visible_child = complete;
- loader_pulsing = false;
- }
-
- private double upload_frac;
- private void upload_callback(uint64 bytes_sent, uint64 bytes_total, Pprf.UploadStatus status) {
- print(@"Got update: $status\n");
- upload_frac = ((double)bytes_sent)/((double)bytes_total);
- switch(status) {
- case Pprf.UploadStatus.INITIATING_SESSION:
- Idle.add_once(() => loader_status.set_text(@"Establishing upload session with server…"));
- break;
- case Pprf.UploadStatus.UPLOADING_CHUNK:
- case Pprf.UploadStatus.UPLOADED_CHUNK:
- Idle.add_once(() => {
- loader_pulsing = false;
- loader_status.set_text(@"Uploading publication…");
- loader_progress.fraction = upload_frac;
- });
- break;
- case Pprf.UploadStatus.UNPUBLISHING:
- Idle.add_once(() => {
- loader_status.set_text(@"Unpublishing previous publication…");
- pulse_loader();
- });
- break;
- case Pprf.UploadStatus.FINALISING_SESSION:
- Idle.add_once(() => {
- loader_status.set_text(@"Finalising upload…");
- pulse_loader();
- });
- break;
- case Pprf.UploadStatus.COMPLETE:
- Idle.add_once(() => {
- loader_status.set_text(@"Upload completed!");
- pulse_loader();
- });
- break;
- }
- }
- }
- }
|