diff options
Diffstat (limited to 'main/src/ui')
| -rw-r--r-- | main/src/ui/chat_input/chat_input_controller.vala | 72 | ||||
| -rw-r--r-- | main/src/ui/chat_input/chat_text_view.vala | 2 | ||||
| -rw-r--r-- | main/src/ui/chat_input/view.vala | 99 | ||||
| -rw-r--r-- | main/src/ui/conversation_content_view/file_default_widget.vala | 2 | ||||
| -rw-r--r-- | main/src/ui/conversation_view_controller.vala | 57 | ||||
| -rw-r--r-- | main/src/ui/file_send_overlay.vala | 69 |
6 files changed, 169 insertions, 132 deletions
diff --git a/main/src/ui/chat_input/chat_input_controller.vala b/main/src/ui/chat_input/chat_input_controller.vala index 660f29bb..c93a9e22 100644 --- a/main/src/ui/chat_input/chat_input_controller.vala +++ b/main/src/ui/chat_input/chat_input_controller.vala @@ -27,6 +27,10 @@ public class ChatInputController : Object { private ChatTextViewController chat_text_view_controller; private ContentItem? quoted_content_item = null; + private ArrayList<File> files_to_send = new ArrayList<File>(); + + private HashMap<Conversation, string> message_cache = new HashMap<Conversation, string>(Conversation.hash_func, Conversation.equals_func); + private HashMap<Conversation, ArrayList<File>> file_cache = new HashMap<Conversation, ArrayList<File>>(Conversation.hash_func, Conversation.equals_func); public ChatInputController(ChatInput.View chat_input, StreamInteractor stream_interactor) { this.chat_input = chat_input; @@ -45,7 +49,12 @@ public class ChatInputController : Object { chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted()); chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed); - chat_text_view_controller.send_text.connect(send_text); + chat_text_view_controller.send_text.connect(send_message); + chat_input.send_button.clicked.connect(send_message); + + chat_input.file_removed.connect((file) => { + remove_file_transfer(file); + }); chat_input.encryption_widget.encryption_changed.connect(on_encryption_changed); @@ -84,12 +93,33 @@ public class ChatInputController : Object { } public void set_conversation(Conversation conversation) { - reset_input_field_status(); - this.quoted_content_item = null; - chat_input.unset_quoted_message(); + Conversation? previous_conversation = this.conversation; + + // Save state of the previous conversation and unset it + if (previous_conversation != null) { + message_cache[previous_conversation] = chat_input.chat_text_view.text_view.buffer.text; + file_cache[previous_conversation] = files_to_send; + + chat_input.chat_text_view.text_view.buffer.text = ""; + reset_input_field_status(); + this.files_to_send = new ArrayList<File>(); + this.quoted_content_item = null; + chat_input.unset_quoted_message(); + chat_input.clear_files(); + } + // Set the new conversation this.conversation = conversation; + if (message_cache.has_key(conversation)) chat_input.chat_text_view.text_view.buffer.text = message_cache[conversation]; + if (file_cache.has_key(conversation)) { + foreach (File file in file_cache[conversation]) chat_input.add_file(file); + files_to_send = file_cache[conversation]; + } + + message_cache.unset(conversation); + file_cache.unset(conversation); + chat_input.encryption_widget.set_conversation(conversation); chat_input.initialize_for_conversation(conversation); @@ -98,6 +128,16 @@ public class ChatInputController : Object { update_moderated_input_status(conversation.account); } + public void add_file_transfer(File file) { + files_to_send.add(file); + chat_input.add_file(file); + } + + private void remove_file_transfer(File file) { + files_to_send.remove(file); + chat_input.remove_file(file); + } + public void set_file_upload_active(bool active) { chat_input.set_file_upload_active(active); } @@ -129,12 +169,6 @@ public class ChatInputController : Object { } private void send_text() { - // Don't do anything if we're in a NO_SEND state. Don't clear the chat input, don't send. - if (input_field_status.input_state == Plugins.InputFieldStatus.InputState.NO_SEND) { - chat_input.highlight_state_description(); - return; - } - string text = chat_input.chat_text_view.text_view.buffer.text; ContentItem? quoted_content_item_bak = quoted_content_item; var markups = chat_input.chat_text_view.get_markups(); @@ -200,6 +234,24 @@ public class ChatInputController : Object { Dino.send_message(conversation, text, quoted_content_item_bak != null ? quoted_content_item_bak.id : 0, null, markups); } + public void send_message() { + // Don't do anything if we're in a NO_SEND state. Don't clear the chat input, don't send. + if (input_field_status.input_state == Plugins.InputFieldStatus.InputState.NO_SEND) { + chat_input.highlight_state_description(); + return; + } + + if (chat_input.chat_text_view.text_view.buffer.text.strip() != "") { + send_text(); + } + + foreach (File file in files_to_send) { + stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation); + } + files_to_send = new ArrayList<File>(); + chat_input.clear_files(); + } + private void on_text_input_changed() { if (chat_input.chat_text_view.text_view.buffer.text != "") { stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_entered(conversation); diff --git a/main/src/ui/chat_input/chat_text_view.vala b/main/src/ui/chat_input/chat_text_view.vala index 201b60ac..5f072e9a 100644 --- a/main/src/ui/chat_input/chat_text_view.vala +++ b/main/src/ui/chat_input/chat_text_view.vala @@ -150,7 +150,7 @@ public class ChatTextView : Box { if ((state & ModifierType.SHIFT_MASK) > 0) { // Let the default handler normally insert a newline if shift was hold return false; - } else if (text_view.buffer.text.strip() != "") { + } else { send_text(); } return true; diff --git a/main/src/ui/chat_input/view.vala b/main/src/ui/chat_input/view.vala index 8b57a08d..24a40033 100644 --- a/main/src/ui/chat_input/view.vala +++ b/main/src/ui/chat_input/view.vala @@ -10,6 +10,8 @@ namespace Dino.Ui.ChatInput { [GtkTemplate (ui = "/im/dino/Dino/chat_input.ui")] public class View : Box { + public signal void file_removed(File file); + public string text { owned get { return chat_text_view.text_view.buffer.text; } set { chat_text_view.text_view.buffer.text = value; } @@ -17,13 +19,16 @@ public class View : Box { private StreamInteractor stream_interactor; private Conversation? conversation; - private HashMap<Conversation, string> entry_cache = new HashMap<Conversation, string>(Conversation.hash_func, Conversation.equals_func); + + private HashMap<File, Widget> file_widgets = new HashMap<File, Widget>(); [GtkChild] public unowned Box quote_box; + [GtkChild] public unowned Box file_box; [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Button file_button; [GtkChild] public unowned MenuButton emoji_button; [GtkChild] public unowned MenuButton encryption_button; + [GtkChild] public unowned Button send_button; [GtkChild] public unowned Separator file_separator; [GtkChild] public unowned Label chat_input_status; [GtkChild] public unowned Box unavailable_box; @@ -54,20 +59,78 @@ public class View : Box { return this; } + public void add_file(File file) { + FileInfo file_info; + try { + file_info = file.query_info("*", FileQueryInfoFlags.NONE); + } catch (Error e) { + warning("Failed querying info for file %s", file.get_path()); + return; + } + var content_type = new Xmpp.FileContentType.from_file_info(file_info); + + bool is_image = Dino.Util.is_pixbuf_supported_content_type(content_type); + + Widget? widget = null; + if (is_image) { + var picture = new FixedRatioPicture() { + file = file, + height_request = 64, + width_request = 64, + content_fit = ContentFit.COVER, + halign = Align.START, + margin_top = 8, + margin_end = 8, + margin_bottom = 4 + }; + picture.add_css_class("image-round-corners"); + + widget = picture; + } else { + FileInputWidget file_widget = new FileInputWidget() { margin_top = 8, margin_end = 8, margin_bottom = 4 }; + file_widget.set_info(file_info, content_type); + widget = file_widget; + } + + Button remove_button = new Button.from_icon_name("dino-window-close-symbolic") { halign=Align.END, valign=Align.START }; + remove_button.add_css_class("file-remove-button"); + remove_button.add_css_class("circular"); + remove_button.add_css_class("opaque"); + + remove_button.clicked.connect(() => { + this.file_removed(file); + }); + + Overlay overlay = new Overlay() { margin_end = 2, halign = Align.START, valign = Align.CENTER }; + overlay.add_css_class("file-share-wrap"); + overlay.add_overlay(remove_button); + overlay.set_child(widget); + + file_box.append(overlay); + + file_widgets[file] = overlay; + } + + public void remove_file(File file) { + Widget? file_widget = file_widgets[file]; + + if (file_widget == null) { + warning("Trying to remove file that doesn't have a widget"); + return; + } + + file_box.remove(file_widget); + file_widgets.unset(file); + } + public void set_file_upload_active(bool active) { file_button.visible = active; file_separator.visible = active; } public void initialize_for_conversation(Conversation conversation) { - if (this.conversation != null) entry_cache[this.conversation] = chat_text_view.text_view.buffer.text; this.conversation = conversation; - chat_text_view.text_view.buffer.text = ""; - if (entry_cache.has_key(conversation)) { - chat_text_view.text_view.buffer.text = entry_cache[conversation]; - } - update_unavailable_message(); do_focus(); @@ -203,9 +266,29 @@ public class View : Box { quote_box.visible = false; } + public void clear_files() { + Widget? file_child = file_box.get_first_child(); + while (file_child != null) { + file_box.remove(file_child); + file_child = file_box.get_first_child(); + } + } + public void do_focus() { chat_text_view.text_view.grab_focus(); } } - } + +[GtkTemplate (ui = "/im/dino/Dino/file_input_widget.ui")] +public class Dino.Ui.FileInputWidget : Box { + [GtkChild] public unowned Label name_label; + [GtkChild] public unowned Label mime_label; + [GtkChild] public unowned Image content_type_image; + + public void set_info(FileInfo file_info, Xmpp.FileContentType? content_type) { + name_label.label = file_info.get_name(); + content_type_image.icon_name = Dino.Ui.FileDefaultWidget.get_file_icon_name(content_type); + mime_label.label = content_type != null ? content_type.get_description() : null; + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala index f211e3fe..a21e2afb 100644 --- a/main/src/ui/conversation_content_view/file_default_widget.vala +++ b/main/src/ui/conversation_content_view/file_default_widget.vala @@ -138,7 +138,7 @@ public class FileDefaultWidget : Box { file_menu.opacity = 0; } - private static string get_file_icon_name(Xmpp.FileContentType? content_type) { + public static string get_file_icon_name(Xmpp.FileContentType? content_type) { if (content_type == null) return "dino-file-symbolic"; string generic_icon_name = content_type.get_generic_icon_name() ?? ""; diff --git a/main/src/ui/conversation_view_controller.vala b/main/src/ui/conversation_view_controller.vala index 2b073e2d..08f9dd5d 100644 --- a/main/src/ui/conversation_view_controller.vala +++ b/main/src/ui/conversation_view_controller.vala @@ -14,7 +14,7 @@ public class ConversationViewController : Object { private Widget? overlay_dialog; public SearchMenuEntry search_menu_entry = new SearchMenuEntry(); public ListView list_view = new ListView(null, null); - private DropTarget drop_event_controller = new DropTarget(typeof(File), DragAction.COPY ); + private DropTarget drop_event_controller = new DropTarget(typeof(FileList), DragAction.COPY ); private ChatInputController chat_input_controller; private StreamInteractor stream_interactor; @@ -182,65 +182,36 @@ public class ConversationViewController : Object { Gdk.Texture? texture = yield clipboard.read_texture_async(null); // TODO critical var file_name = Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png"); texture.save_to_png(file_name); - open_send_file_overlay(File.new_for_path(file_name)); + chat_input_controller.add_file_transfer(File.new_for_path(file_name)); } catch (IOError.NOT_SUPPORTED e) { // Format not supported, ignore } } private bool on_drag_data_received(DropTarget target, Value val, double x, double y) { - if (val.type() == typeof(File)) { - open_send_file_overlay((File)val); + if (val.type() == typeof(FileList)) { + foreach (File file in ((FileList)val).get_files()) { + chat_input_controller.add_file_transfer(file); + } return true; } return false; } private void open_file_picker() { - FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); + FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")) { + select_multiple = true + }; chooser.response.connect((response) => { if (response == ResponseType.ACCEPT) { - open_send_file_overlay(chooser.get_file()); - } - }); - chooser.show(); - } - - private void open_send_file_overlay(File file) { - FileInfo file_info; - try { - file_info = file.query_info("*", FileQueryInfoFlags.NONE); - } catch (Error e) { - warning("Failed querying info for file %s", file.get_path()); - return; - } - - FileSendOverlay file_send_overlay = new FileSendOverlay(file, file_info); - file_send_overlay.send_file.connect(send_file); - - stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.begin(conversation, (_, res) => { - HashMap<int, long> limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.end(res); - bool something_works = false; - foreach (var limit in limits.values) { - if (limit >= file_info.get_size()) { - something_works = true; - } - } - if (!something_works && limits.has_key(0)) { - if (!something_works && file_info.get_size() > limits[0] && file_send_overlay != null) { - file_send_overlay.set_file_too_large(); + var files_model = chooser.get_files(); + for (int i = 0; i < files_model.get_n_items(); i++) { + File file = (File) files_model.get_item(i); + chat_input_controller.add_file_transfer(file); } } }); - - file_send_overlay.closed.connect(() => { - // We don't want drag'n'drop to be active while the overlay is active - update_file_upload_status.begin(); - }); - - file_send_overlay.present(view); - - update_file_upload_status.begin(); + chooser.show(); } private void send_file(File file) { diff --git a/main/src/ui/file_send_overlay.vala b/main/src/ui/file_send_overlay.vala deleted file mode 100644 index 907cea00..00000000 --- a/main/src/ui/file_send_overlay.vala +++ /dev/null @@ -1,69 +0,0 @@ -using Gee; -using Gdk; -using Gtk; - -using Dino.Entities; - -namespace Dino.Ui { - -[GtkTemplate (ui = "/im/dino/Dino/file_send_overlay.ui")] -public class FileSendOverlay : Adw.Dialog { - - public signal void send_file(File file); - - [GtkChild] protected unowned Button send_button; - [GtkChild] protected unowned SizingBin file_widget_insert; - [GtkChild] protected unowned Label info_label; - - private File file; - private bool can_send = true; - - construct { - check_widget_leak(this); - } - - public FileSendOverlay(File file, FileInfo file_info) { - this.file = file; - load_file_widget.begin(file, file_info); - } - - [GtkCallback] - private void on_send_button_clicked() { - send_file(file); - close(); - } - - private async void load_file_widget(File file, FileInfo file_info) { - string file_name = file_info.get_display_name(); - var content_type = new Xmpp.FileContentType.from_file_info(file_info); - - bool is_image = Dino.Util.is_pixbuf_supported_content_type(content_type); - - Widget? widget = null; - if (is_image) { - FileImageWidget image_widget = new FileImageWidget(); - try { - yield image_widget.load_from_file(file, file_name); - widget = image_widget; - } catch (Error e) { } - } - - if (widget == null) { - FileDefaultWidget default_widget = new FileDefaultWidget(); - default_widget.name_label.label = file_name; - default_widget.set_static_file_info(content_type); - widget = default_widget; - } - - widget.set_parent(file_widget_insert); - } - - public void set_file_too_large() { - info_label.label= _("The file exceeds the server's maximum upload size."); - Util.force_error_color(info_label); - send_button.sensitive = false; - can_send = false; - } -} - -} |
