Compare commits

...

10 Commits

Author SHA1 Message Date
Xavier Del Campo Romero 780df43c83
WIP chunk signals 2023-10-04 03:01:31 +02:00
Xavier Del Campo Romero 38b075873f
http-files: Avoid null dereferences 2023-10-04 01:28:33 +02:00
Xavier Del Campo Romero 2e5f9c5ba2
WIP reconnect stream 2023-10-04 01:28:33 +02:00
Xavier Del Campo Romero 1aaafc1af4
history_sync.vala: Remove noisy debug 2023-10-04 01:28:33 +02:00
Xavier Del Campo Romero 45b74d9a4b
Add screenshots 2023-10-04 01:28:33 +02:00
Xavier Del Campo Romero f01dd81c73
README.md: Update features 2023-10-04 01:28:33 +02:00
Xavier Del Campo Romero 7a31d74581
Allow to copy messages 2023-10-04 01:28:32 +02:00
Xavier Del Campo Romero a9d1b5fb43
Add BACKPORT 2023-10-04 01:28:32 +02:00
Xavier Del Campo Romero 6a152d4e4c
Show warning dialog on failed GTK video sink creation 2023-10-04 01:28:32 +02:00
Xavier Del Campo Romero 12cf6ada70
Make Ctrl-Q accelerator user-configurable
Some devices, such as those using virtual keyboards such as Squeekboard,
might find the Ctrl-Q accelerator annoying, as it can be easily
mistyped.
2023-10-04 01:28:32 +02:00
23 changed files with 558 additions and 37 deletions

343
BACKPORT Normal file
View File

@ -0,0 +1,343 @@
This fork of Dino diverged from upstream on this commit:
f25bfb00969a7e09996da2d5500e6718f4cc0148
However, this fork of Dino aims to backport all compatible security
fixes and improvements from upstream. This is an exhaustive list of
every commit that was reviewed and its status, plus additional
comments:
7e7dcedaf31ee35499875491c9f569c575d28435: Not applied.
As its commit message suggests, this commit ported Dino from GTK3 to
GTK4, but this fork will keep using GTK3 as a requirement.
2b3ce5fc95c63ed7d54e207db0585c8b8bbcd603: Not applied.
GTK4-related changes are out of scope for this fork.
f44cbe02c17df1f02ad49c63cd784fec0ea02d85: Not applied.
GTK4-related changes are out of scope for this fork.
e51b55432fe98e0fbc036fe785ef50fbf1589034: Not applied.
GTK4-related changes are out of scope for this fork.
0af92393f134c59646deaab7d963a32c0d3d58b2: Not applied.
GTK4-related changes are out of scope for this fork.
517363dfc9dab81c4b59310c739bcf71266fabe0: Not applied.
GTK4-related changes are out of scope for this fork.
6bfa70fc7045669be435d3757456e1da7d341927: Not applied.
GTK4-related changes are out of scope for this fork.
5103a7fb7b32fcee6656dbae0eec54fb26f89d80: Not applied.
The resource tree for Dino got changed during the GTK4 port, including
some icons. However, this commit is not needed for this fork.
7b252d040a845b7e9c515f61927fbf5bf7ed4c3a: Partially applied.
However, it had to be slightly adapted to the dependencies as listed
by our version of plugins/rtp/CMakeLists.txt.
d6afa6e8ff4cfb533140d9434b83f18f627f11ca: Not applied.
GTK4-related changes are out of scope for this fork.
054d3fec1627a9ee8d84d636d578aa68d3494d19: Not applied.
None of these changes seem required or seem to fix anything in our
fork.
e85477bb19166e742e34852b2a99b33e55565ded: Not applied.
I am unsure whether this commit would break anything, and so far I had
no problems on 0.56.0, which is older than 0.56.1.
Also, these changes were introduced by the change from GTK3 to GTK4.
Some further reference: https://github.com/dino/dino/pull/1234.
0aa73c4569a90031d8a88173f8689c18d02ddbe2: Not applied.
Because of the same reasons as e85477bb19166e742e34852b2a99b33e55565ded.
14bc3d6717515e0b34b02ef5b5ad4c3ec52ccdc2: Not applied.
This is regression introduced by
f44cbe02c17df1f02ad49c63cd784fec0ea02d85, which was part of the effort
to port Dino to GTK4.
117f19381233207e4b5aef03c82e7dd4b2d1debd: Not applied.
Again, these look like regressions introduced with the migration to
GTK4.
b8e84c83268a11ae41ad1d673999362427fd755c: Applied.
7ad52d9335579d03613036a7da9967fcf0c5d6b3: Not applied.
This commit looks incompatible with GTK3, but I might be wrong.
146af3152475f12c9b19a92c4779a53f6fc517ce: Not applied.
Some resources were moved when porting Dino to GTK4, but this does not
apply to our fork.
21ab48e09aa6b0ade8f25bdc93f89f8d3aa462e7: Not applied.
It looks like this is trying to solve a regression introduced during
the migration from GTK3 to GTK4, as the calls to
jid_entry.key_release_event.connect() and
nick_entry.key_release_event.connect() are not commented out on this
fork.
85342ee2eb2aa3e6d7599c503d17c00d861bafcb: Not applied.
Drag and drop uploading always forked for this fork.
03878eee495cac8dcc8baf0ff4f84e4c9e76114e: Not applied.
These were disabled during the migration from GTK3 to GTK4, so this
commit does not apply to this fork.
7d8b08deca0aa4eb24def6b9af4ec180c0bc9a27: Partially applied.
Whereas the changes on list_row.ui caused many conflicts, the null
check on conversation_view_controller.vala is required.
9c736af765d8c62838440afbfd2ad7ee78b44951: Not applied.
check_if_done() was introduced by
21ab48e09aa6b0ade8f25bdc93f89f8d3aa462e7, which was not applied,
either.
6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92: Partially applied.
Almost everything could be cleanly backported, except from
main/src/ui/add_conversation/conference_list.vala, where the original
implementation was preferred.
80258a874ddfeb87b4b71f5791eab94a2465de6d: Not applied.
I have no personal interest in XMPP reactions for this fork, as other
popular XMPP clients such as Conversations are still rely on styling as
described on https://xmpp.org/extensions/xep-0393.html#quote.
bc5a1d35cbf5c1aca406fa0fe81ca60d6b280bd5: Applied.
7e0d1db1965555720db2bef7380e61c23ef6dbcd: Applied.
f82f788f43e385391db2827cde151830fc91bc14: Applied.
1bf57a42fa5c36977132d21f59ca6637fcd0c3d3: Applied.
11b6e615b73e4183a06f9d456634c44ead612336: Not applied.
GTK4-related changes are out of scope for this fork.
09829b33824ab7d1fbf9886b7ed3e42cd8c34ff2: Not applied.
This commit fixes a regression introduced by
80258a874ddfeb87b4b71f5791eab94a2465de6d, which is related to
reactions, something this fork does not implement.
a45280f8dfe45f8908b44cd13996316af44117e9: Not applied.
This commit is related to reactions, which this fork does not
implement.
2ab7374aa53f70b30765a02865d92e6d71c6e623: Not applied.
This commit is related to reactions, which this fork does not
implement.
a2f2224781a82121c86a1f19b309245bc8369a91: Applied.
809c1579e41000f2f43eeb05735afc8165a1a430: Applied.
According to the commit message, the changes introduced by this commit
were meant for libsoup3 only. However, the commit did not wrap the
libsoup3-specific in #if SOUP_3, so this has been added for this
fork.
6e37f3fe3fa0f4ce9a25a91e9d97191c8e4abec1: Applied.
e62955d3cf266a7f7ff0f2085a64f1c99021127c: Applied.
7a19a25156a73e7e6b6d77fabc7621e7d2c443f0: Partially applied.
The changes to libdino/src/service/reactions.vala are out of scope for
this fork.
a2e894dda132f1679ee8e9998879be6bda7ab320: Applied.
cdd22e404eca3db640b6f2f2789314f7cbb65de6: Not applied.
GTK4-related changes are out of scope for this fork.
d1fb22ebedca7dbbbd0f693baa3c38d99ab5c344: Not applied.
This commit is related to reactions, which this fork does not
implement.
4d50c51a75de70c0f30a196e1f128154ba1651fa: Applied.
30f99d1347f8f5e2db364a25910d76b0faf2ea36: Applied.
799d09a4c98d1a00790f261600d3f4d813140954: Applied.
4d7809bb12199a598b531ca3ca019a4bb5a867f7: Partially applied.
- Too many changes are introduced to
main/src/ui/chat_input/encryption_button.vala, which might break this
fork.
- Changes related to own_occupant_ids are
related to reactions (as introduced by
80258a874ddfeb87b4b71f5791eab94a2465de6d), and thus are out of scope
for this fork.
- This commit removes plugins/omemo/src/ui/manage_key_dialog.vala, which
this fork still relies on.
- headerbar is still used by plugins/omemo/src/ui/manage_key_dialog.vala,
and thus should not be removed.
- Too many changes are introduced to
main/src/ui/conversation_content_view/conversation_item_skeleton.vala,
which might break this fork.
- The changes in
main/src/ui/conversation_content_view/conversation_view.vala break
the build.
- The changes in
main/src/ui/conversation_view_controller.vala break the build.
dc52e7595cca06d0a2da7d11b3c88cb2f7ce529c: Not applied.
I am not interested in XEP-0461 (replies) for this fork, as the
fallback implementations is already provided here.
60371331e0882758b0b9c2efedb3821e716defd7: Not applied.
Replies are out of scope for this fork.
424a4290622246303a7b73410d7e4a5a6d57dd6b: Not applied.
Replies are out of scope for this fork.
0c4aea96ffbc05d6efeb9a83424b872ce7f30d88: Not applied.
Replies are out of scope for this fork.
cb3b19b01deb8460627578b885339e7528411f6f: Not applied.
Replies are out of scope for this fork.
75500dc767f2cf657c0fbb5d2a4d4557183ed2e9: Not applied.
This commit breaks the build in some places, and I am not particulary
interested in conversation pinning anyway.
860c72bfc93d252d45eb97e71cf9ff22985c7ef9: Not applied.
This commit fixes regressions introduced by other commits that were not
introduced by this fork, such as
7e7dcedaf31ee35499875491c9f569c575d28435.
73c0263f35a73b68d20d299ee7fe8c37b9a6ffeb: Not applied.
This commit depends on other commits that were not introduced by this
fork, such as cb3b19b01deb8460627578b885339e7528411f6f, which was
related to replies (not implemented by this fork).
05289e0b4dc9bc076955e27b30b386cb7f0604c7: Not applied.
Replies are out of scope for this fork.
7da79864b384c9370a5937d480230e771834d91a: Not applied.
Conversation pinning is out of scope for this fork.
7e0d1db1965555720db2bef7380e61c23ef6dbcd: Applied.
f6e73d85c00a60a719da95a048ba2c15712325c3: Not applied.
GTK4-related changes are out of scope for this fork.
1ef42b47d22d21600ccf1e2d8b4d80605448660d: Not applied.
GTK4-related changes are out of scope for this fork.
2741bf21ae6d53324a512dacef65d540be840fe4: Not applied.
GTK4-related changes are out of scope for this fork.
ba9462503c0561dbe8306e3bf6aa49392bfc8078: Not applied.
GTK4-related changes are out of scope for this fork.
04acab82c98f5d9cdb798ba3baac8d73b097b1df: Not applied.
GTK4-related changes are out of scope for this fork.
e934a76a1139938ae668836b812102cd5d9c9d9f: Not applied.
GTK4-related changes are out of scope for this fork.
Also, this fork already has a back button for ConversationTitleBar.
0d7c8bb6e117f8cdd631730302413aad21632c2d: Not applied.
GTK4-related changes are out of scope for this fork.
92aca5672db723121471e513e83b68742761d1b5: Not applied.
GTK4-related changes are out of scope for this fork.
4b391f3f31c2272be11a24c8301641b045260e99: Not applied.
GTK4-related changes are out of scope for this fork.
ef98adb18a016dba65162602eb336fb82c64805e: Not applied.
GTK4-related changes are out of scope for this fork.
6a182ba313026b93d54a9d2246a0ab68894c6833: Not applied.
GTK4-related changes are out of scope for this fork.
99d9cb383abb1a33f6d0572deb4292dbf358f3ce: Not applied.
GTK4-related changes are out of scope for this fork.
cc7db3b85f7b29bfac333937d8bf09a81d8dc4a5: Not applied.
GTK4-related changes are out of scope for this fork.
e35df88d4a00c3a34f2b4d9fb7f10bb5d877bd29: Not applied.
GTK4-related changes are out of scope for this fork.
26be9d4bb40b223cb8a657b03e6457988a8cc269: Not applied.
Reactions are out of scope for this fork.
5d9978b38bb0e729dcccecddd08cc59e5585a6cc: Not applied.
Reactions are out of scope for this fork.
c813a6d2405980c71450cefa10abdf11fab8e995: Not applied.
GTK4-related changes are out of scope for this fork.
e833a924b5a66048506b4e0b0885ce2e35cac6fc: Applied.
e833a924b5a66048506b4e0b0885ce2e35cac6fc: Applied.
04eb0e763b0e02ea1bc698f1ccb24f84b0154010: Not applied.
This commit is way too big to merge and is tighly coupled to upstream
source code. Maybe a similar commit can be provided independently.
d0a00e1e7549609f7d83e7b432f7f547a1fcebb0: Applied.
1dbacbbcab139d0f8036446441ad143ef7e7eb30: Applied.
10a2bce5122dcd1e6fef037633a26568bf27d4d1: Applied.
b0b81b88c6948dcfd2b1b82a9fe7357316a3af1f: Not applied.
Both reactions and replies are out of scope for this fork.
e3c833bce0713e9a0290841306c7727dfc1e3860: Applied.
This fork defines different logic for update_received_mark(), so the
commit was adapted to it.
1e23b7bbd2a66e5ff40ae5fc5aa6523fa604f242: Not applied.
Replies are out of scope for this fork.
9e11bef219880b5bdc5d299c31ec6249596a86ba: Not applied.
string_if_tooltips_active() was added by
6bfa70fc7045669be435d3757456e1da7d341927, which is GTK4-related.
921f28c84bbda56ec93df7dcde7c828eaabb0b58: Not applied.
Reactions are out of scope for this fork.
3aa3912dc3ea740a5b93f8b694ead45e1b655238: Not applied.
main/src/ui/widgets/date_separator.vala depends on GTK4 libadwaita.
e0ece2aa62aa6b8350be83c787ea003a75f07437: Partially applied.
The changes to main/data/unified_main_content.ui and
main/src/ui/conversation_selector/conversation_selector.vala depend on
other commits related to GTK4 and libadwaita.
95fefaff51e5506d3f0e5fe8bced14aeb3fbe037: Applied.
18321ed15ce782ff5d1f24de9f2fb459d714d125: Partially applied.
The changes to libdino/src/service/reactions.vala and
xmpp-vala/src/module/xep/0444_reactions.vala are not relevant to this
fork.
d76e12b215eb62e4eda5a0f92fbf5c1bd7c1848e: Applied.
f74c1f18b12df0d650f74b6fa43b7f2f0a9bce79: Applied.
1d123c7e66d963fd8cc8cc4250b5813a62676f56: Applied.
d092473fe401a5a668e57f054efbd1e84ac6ca59: Applied.
1559a7a60370c2aa0203e5c4222def4ae3258006: Applied.
32ae87a3c4cebaa05e0e702d744900cd414000db: Not applied.
Seems to cause many conflicts while providing little benefit.
116682e311edca6665a0497c8b225b4fe69859a7: Partially applied.
Changes to main/src/ui/conversation_content_view/quote_widget.vala are
not relevant to this fork.
9c5e36020d8997452d4fd07c5a153e1e7fc24088: Applied.
e73b556a1ae5ea4af41c610f7b05545cf60d59c4: Not applied.
This commit is way too big to merge and is tighly coupled to upstream
source code. Maybe a similar commit can be provided independently.
5568bbc6bf505c4f8ea93fc460dbeff6f4d36e15: Partially applied.
Changes related to libadwaita are out of scope for this fork.

View File

@ -1,10 +1,21 @@
# An unbranded Dino fork
## Screenshots
![Screenshot showing mobile form factor and OMEMO-by-default support](doc/screenshot-2.png)
![Screenshot showing a MUC room in desktip form factor](doc/screenshot-1.png)
## Features
- **OMEMO encryption set by default**.
- Portrait screen support with `libhandy`.
- Improved [`connection_manager.vala`](libdino/src/service/connection_manager.vala).
- Quote messages according to XEP-0393 Message Styling.
- Copy messages.
- Optional send button and Enter-to-send accelerator.
- Keeps using GTK3.
- Includes some features that did not make into upstream.
- Backports bugfixes and improvements from upstream.
- Backports bugfixes and improvements from upstream (see [`BACKPORT`](BACKPORT)).
## TODO

BIN
doc/screenshot-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
doc/screenshot-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -3,7 +3,7 @@ using Xmpp;
namespace Dino.Entities {
public class FileTransfer : Object {
public signal void progress_updated(int progress);
public const bool DIRECTION_SENT = true;
public const bool DIRECTION_RECEIVED = false;
@ -70,6 +70,14 @@ public class FileTransfer : Object {
public State state { get; set; default=State.NOT_STARTED; }
public int provider { get; set; }
public string info { get; set; }
public int progress_ { get; set; }
public int progress {
get {return progress_;}
set {
progress_ = value;
progress_updated(progress_);
}
}
public Cancellable cancellable { get; default=new Cancellable(); }
private Database? db;
@ -103,6 +111,7 @@ public class FileTransfer : Object {
state = (State) row[db.file_transfer.state];
provider = row[db.file_transfer.provider];
info = row[db.file_transfer.info];
progress = row[db.file_transfer.progress];
notify.connect(on_update);
}
@ -124,7 +133,8 @@ public class FileTransfer : Object {
.value(db.file_transfer.size, size)
.value(db.file_transfer.state, state)
.value(db.file_transfer.provider, provider)
.value(db.file_transfer.info, info);
.value(db.file_transfer.info, info)
.value(db.file_transfer.progress, progress);
if (file_name != null) builder.value(db.file_transfer.file_name, file_name);
if (path != null) builder.value(db.file_transfer.path, path);
@ -169,6 +179,8 @@ public class FileTransfer : Object {
update_builder.set(db.file_transfer.provider, provider); break;
case "info":
update_builder.set(db.file_transfer.info, info); break;
case "progress":
update_builder.set(db.file_transfer.progress, progress); break;
}
update_builder.perform();
}

View File

@ -15,6 +15,7 @@ public class Settings : Object {
default_encryption = col_to_encryption_or_default("default_encryption", Encryption.OMEMO);
send_button = col_to_bool_or_default("send_button", false);
enter_newline = col_to_bool_or_default("enter_newline", false);
quit_ctrl_q = col_to_bool_or_default("quit_ctrl_q", true);
}
private bool col_to_bool_or_default(string key, bool def) {
@ -126,6 +127,20 @@ public class Settings : Object {
enter_newline_ = value;
}
}
public signal void quit_ctrl_q_update(bool active);
private bool quit_ctrl_q_;
public bool quit_ctrl_q {
get { return quit_ctrl_q_; }
set {
db.settings.upsert()
.value(db.settings.key, "quit_ctrl_q", true)
.value(db.settings.value, value.to_string())
.perform();
quit_ctrl_q_ = value;
quit_ctrl_q_update(value);
}
}
}
}

View File

@ -252,11 +252,32 @@ public class ConnectionManager : Object {
}
}
private void check_ping_reconnect(Xmpp.Xep.Ping.Module identity,
XmppStream stream, Account account) {
identity.send_ping.begin(stream, account.bare_jid.domain_jid, () => {
connections[account].acked = true;
if (connections[account].stream != stream) return;
connect_stream.begin(account);
});
Timeout.add_seconds(10, () => {
if (!connections.has_key(account)) return false;
if (connections[account].stream != stream) return false;
if (connections[account].acked) return false;
// Reconnect. Nothing gets through the stream.
debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream);
change_connection_state(account, ConnectionState.DISCONNECTED);
connections[account].reset();
connect_stream.begin(account);
return false;
});
}
private void check_reconnect(Account account, bool directly_reconnect = false) {
if (!connections.has_key(account)) return;
XmppStream? stream = connections[account].stream;
var cancellable = new Cancellable();
debug(@"account.domainpart=$(account.domainpart)");
@ -267,29 +288,23 @@ public class ConnectionManager : Object {
debug(@"can-reach: $(reachable)");
if (reachable) {
Xmpp.Xep.Ping.Module? identity = null;
if (connections[account].connection_state == ConnectionState.CONNECTING) return;
XmppStream? stream = connections[account].stream;
connections[account].acked = false;
change_connection_state(account, ConnectionState.CONNECTING);
stream.get_module(Xep.Ping.Module.IDENTITY).send_ping.begin(stream, account.bare_jid.domain_jid, () => {
connections[account].acked = true;
if (connections[account].stream != stream) return;
change_connection_state(account, ConnectionState.CONNECTED);
});
Timeout.add_seconds(10, () => {
if (!connections.has_key(account)) return false;
if (connections[account].stream != stream) return false;
if (connections[account].acked) return false;
// Reconnect. Nothing gets through the stream.
debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream);
if (stream != null
&& (identity = stream.get_module(Xep.Ping.Module.IDENTITY)) != null) {
change_connection_state(account, ConnectionState.CONNECTING);
check_ping_reconnect(identity, stream, account);
}
else {
change_connection_state(account, ConnectionState.DISCONNECTED);
connections[account].reset();
connect_stream.begin(account);
return false;
});
}
}
} catch (Error e) {

View File

@ -147,11 +147,12 @@ public class Database : Qlite.Database {
public Column<int> state = new Column.Integer("state");
public Column<int> provider = new Column.Integer("provider");
public Column<string> info = new Column.Text("info");
public Column<int> progress = new Column.Integer("progress");
internal FileTransferTable(Database db) {
base(db, "file_transfer");
init({id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time,
encryption, file_name, path, mime_type, size, state, provider, info});
encryption, file_name, path, mime_type, size, state, provider, info, progress});
}
}

View File

@ -119,7 +119,19 @@ public class FileManager : StreamInteractionModule, Object {
file_send_data = file_encryptor.preprocess_send_file(conversation, file_transfer, file_send_data, file_meta);
}
size_t sent_bytes = 0;
file_sender.progress.connect((length) => {
if (file_transfer.size != 0) {
sent_bytes += length;
file_transfer.progress =
(int)(((uint64)sent_bytes * (uint64)100)
/ (uint64)file_transfer.size);
debug(@"length=$(length), sent_bytes=$(sent_bytes), progress=$(file_transfer.progress)");
}
});
yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta);
file_transfer.state = FileTransfer.State.COMPLETE;
} catch (Error e) {
warning("Send file error: %s", e.message);
@ -292,6 +304,7 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.file_name = file_meta.file_name;
file_transfer.size = (int)file_meta.size;
file_transfer.info = info;
file_transfer.progress = 0;
var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta);
if (encryption != Encryption.NONE) file_transfer.encryption = encryption;
@ -328,7 +341,7 @@ public class FileManager : StreamInteractionModule, Object {
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
file_transfer.state = FileTransfer.State.COMPLETE;
file_transfer.state = FileTransfer.State.IN_PROGRESS;
file_transfer.path = filename;
file_transfer.input_stream = yield file.read_async();
} catch (Error e) {
@ -391,6 +404,7 @@ public interface FileProvider : Object {
public interface FileSender : Object {
public signal void upload_available(Account account);
public signal void progress(size_t length);
public abstract async bool is_upload_available(Conversation conversation);
public abstract async long get_file_size_limit(Conversation conversation);

View File

@ -537,7 +537,6 @@ public class Dino.HistorySync {
if (to_delete.contains(range1)) continue;
foreach (MamRange range2 in ranges[server_jid]) {
debug("[%s | %s] | %s - %s vs %s - %s", account.bare_jid.to_string(), server_jid.to_string(), range1.from_time.to_string(), range1.to_time.to_string(), range2.from_time.to_string(), range2.to_time.to_string());
if (range1 == range2 || to_delete.contains(range2)) continue;
// Check if range2 is a subset of range1

View File

@ -172,6 +172,18 @@
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="quit_ctrl_q_checkbutton">
<property name="label" translatable="yes">Quit Dino with Ctrl+Q</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">8</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object>

View File

@ -117,6 +117,8 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
}
private void create_actions() {
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
SimpleAction accounts_action = new SimpleAction("accounts", null);
accounts_action.activate.connect(show_accounts_window);
add_action(accounts_action);
@ -132,7 +134,20 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
SimpleAction quit_action = new SimpleAction("quit", null);
quit_action.activate.connect(quit);
add_action(quit_action);
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
if (settings.quit_ctrl_q) {
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
}
settings.quit_ctrl_q_update.connect((active) =>
{
if (active) {
set_accels_for_action("app.quit", KEY_COMBINATION_QUIT);
}
else {
set_accels_for_action("app.quit", {null});
}
});
SimpleAction open_conversation_action = new SimpleAction("open-conversation", VariantType.INT32);
open_conversation_action.activate.connect((variant) => {

View File

@ -11,6 +11,7 @@ namespace Dino.Ui.ConversationSummary {
public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins.NotificationCollection {
public signal void on_quote_text(string text);
public signal void on_copy_text(string text);
public Conversation? conversation { get; private set; }
[GtkChild] public unowned ScrolledWindow scrolled;
@ -330,6 +331,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins
MessageMetaItem current_item = item as MessageMetaItem;
current_item.on_quote_text.connect((t, text) => on_quote_text(text));
current_item.on_copy_text.connect((t, text) => on_copy_text(text));
}
meta_items.add(item);
}

View File

@ -31,7 +31,8 @@ public class FileDefaultWidget : EventBox {
cancel_button = new ModelButton() { text=_("Cancel"), visible=true };
}
public void update_file_info(string? mime_type, FileTransfer.State state, long size) {
public void update_file_info(string? mime_type, int progress,
bool direction, FileTransfer.State state, long size) {
this.state = state;
spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap
@ -58,7 +59,12 @@ public class FileDefaultWidget : EventBox {
popover_menu.closed.connect(on_pointer_left);
break;
case FileTransfer.State.IN_PROGRESS:
mime_label.label = _("Downloading %s…").printf(get_size_string(size));
if (direction == FileTransfer.DIRECTION_SENT) {
mime_label.label = _("Uploading %s (%d%%)…").printf(get_size_string(size), progress);
}
else {
mime_label.label = _("Downloading %s (%d)…").printf(get_size_string(size), progress);
}
spinner.active = true;
image_stack.set_visible_child_name("spinner");

View File

@ -148,6 +148,7 @@ public class FileDefaultWidgetController : Object {
this.notify["file-transfer-state"].connect(update_file_info);
this.notify["file-transfer-mime-type"].connect(update_file_info);
file_transfer.progress_updated.connect(update_file_info);
update_file_info();
}
@ -155,13 +156,15 @@ public class FileDefaultWidgetController : Object {
file_uri = file.get_uri();
state = FileTransfer.State.COMPLETE;
widget.name_label.label = this.file_name = file_name;
widget.update_file_info(mime_type, state, -1);
widget.update_file_info(mime_type, 0, false, state, -1);
}
private void update_file_info() {
file_uri = file_transfer.get_file().get_uri();
state = file_transfer.state;
widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
widget.update_file_info(file_transfer.mime_type,
file_transfer.progress,
file_transfer.direction, file_transfer.state, file_transfer.size);
}
private void open_file() {

View File

@ -11,6 +11,7 @@ namespace Dino.Ui.ConversationSummary {
public class MessageMetaItem : ContentMetaItem {
public signal void on_quote_text(string text);
public signal void on_copy_text(string text);
private StreamInteractor stream_interactor;
private MessageItemWidget message_item_widget;
private MessageItem message_item;
@ -56,6 +57,14 @@ public class MessageMetaItem : ContentMetaItem {
};
actions.add(action2);
Plugins.MessageAction copy_action = new Plugins.MessageAction();
copy_action.icon_name = "edit-copy";
copy_action.callback = (button, content_meta_item_activated, widget) => {
string text = ((MessageItem) message_item_widget.content_item).message.body;
on_copy_text(text);
};
actions.add(copy_action);
if (allowed && !in_edit_mode) {
Plugins.MessageAction action1 = new Plugins.MessageAction();
action1.icon_name = "document-edit-symbolic";

View File

@ -14,10 +14,12 @@ public class ConversationView : Gtk.Overlay {
[GtkChild] public unowned ChatInput.View chat_input;
[GtkChild] public unowned ConversationSummary.ConversationView conversation_frame;
[GtkChild] public unowned Revealer white_revealer;
public signal void copy_text(string text);
construct {
white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed);
conversation_frame.on_quote_text.connect((t, text) => on_quote_text(text));
conversation_frame.on_copy_text.connect((t, text) => copy_text(text));
}
public void on_quote_text(string text) {

View File

@ -74,7 +74,8 @@ public class FileSendOverlay : Gtk.EventBox {
if (widget == null) {
FileDefaultWidget default_widget = new FileDefaultWidget() { visible=true };
default_widget.name_label.label = file_name;
default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size());
default_widget.update_file_info(mime_type, 100, FileTransfer.DIRECTION_SENT,
FileTransfer.State.COMPLETE, (long)file_info.get_size());
widget = default_widget;
}

View File

@ -78,6 +78,12 @@ public class MainWindow : Gtk.Window {
search_entry = (SearchEntry) builder.get_object("search_entry");
Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image");
conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/icons/dino-conversation-list-placeholder-arrow.svg"));
conversation_view.copy_text.connect((text) => {
var display = get_display();
var clipboard = Gtk.Clipboard.get_default(display);
clipboard.set_text(text, text.length);
});
}
private void update_headerbar() {

View File

@ -16,6 +16,7 @@ class SettingsDialog : Dialog {
[GtkChild] private unowned RadioButton encryption_radio_openpgp;
[GtkChild] private unowned CheckButton send_button_checkbutton;
[GtkChild] private unowned CheckButton enter_newline_checkbutton;
[GtkChild] private unowned CheckButton quit_ctrl_q_checkbutton;
Dino.Entities.Settings settings = Dino.Application.get_default().settings;
@ -33,6 +34,7 @@ class SettingsDialog : Dialog {
send_button_checkbutton.active = settings.send_button;
enter_newline_checkbutton.active = settings.enter_newline;
enter_newline_checkbutton.sensitive = settings.send_button;
quit_ctrl_q_checkbutton.active = settings.quit_ctrl_q;
typing_checkbutton.toggled.connect(() => { settings.send_typing = typing_checkbutton.active; } );
marker_checkbutton.toggled.connect(() => { settings.send_marker = marker_checkbutton.active; } );
@ -67,6 +69,7 @@ class SettingsDialog : Dialog {
enter_newline_checkbutton.active = visible;
}
});
quit_ctrl_q_checkbutton.toggled.connect(() => { settings.quit_ctrl_q = quit_ctrl_q_checkbutton.active; });
}
}

View File

@ -117,7 +117,9 @@ public class FileProvider : Dino.FileProvider, Object {
});
file_meta.mime_type = content_type;
if (content_length != null) {
file_meta.size = int64.parse(content_length);
if (int64.try_parse(content_length, out file_meta.size) == false) {
throw new FileReceiveError.GET_METADATA_FAILED("failed to parse size");
}
}
return file_meta;
@ -134,6 +136,17 @@ public class FileProvider : Dino.FileProvider, Object {
var session = new Soup.Session();
session.user_agent = @"Dino/$(Dino.get_short_version()) ";
var get_message = new Soup.Message("GET", http_receive_data.url);
size_t received_bytes = 0;
get_message.wrote_body_data.connect((chunk) => {
debug(@"length=$(chunk.length), received_bytes=$(received_bytes), progress=$(file_transfer.progress)");
if (file_transfer.size != 0) {
received_bytes += chunk.length;
file_transfer.progress =
(int)(((uint64)received_bytes * (uint64)100)
/ (uint64)file_transfer.size);
debug(@"length=$(chunk.length), received_bytes=$(received_bytes), progress=$(file_transfer.progress)");
}
});
try {
#if SOUP_3_0
@ -152,10 +165,23 @@ public class FileProvider : Dino.FileProvider, Object {
}
public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation");
var? cm_identity = stream_interactor.get_module(ConversationManager.IDENTITY);
if (cm_identity == null) {
throw new FileReceiveError.GET_METADATA_FAILED("null ConversationManager");
}
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
Conversation? conversation = cm_identity.get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation");
else if (file_transfer.info == null) {
throw new FileReceiveError.GET_METADATA_FAILED("null file_transfer.info");
}
int info;
bool result = int.try_parse(file_transfer.info, out info);
if (result == false) {
throw new FileReceiveError.GET_METADATA_FAILED("failed to parse file_transfer.info");
}
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(info, conversation);
if (message == null) throw new FileReceiveError.GET_METADATA_FAILED("No message");
var file_meta = new HttpFileMeta();
@ -170,10 +196,20 @@ public class FileProvider : Dino.FileProvider, Object {
}
public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
var? cm_identity = stream_interactor.get_module(ConversationManager.IDENTITY);
if (cm_identity == null) return null;
Conversation? conversation = cm_identity.get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
if (conversation == null) return null;
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
var? ms_identity = stream_interactor.get_module(MessageStorage.IDENTITY);
if (ms_identity == null || file_transfer.info == null) return null;
int info;
bool result = int.try_parse(file_transfer.info, out info);
if (result == false) return null;
Message? message = ms_identity.get_message_by_id(info, conversation);
if (message == null) return null;
var receive_data = new HttpFileReceiveData();

View File

@ -81,6 +81,10 @@ public class HttpFileSender : FileSender, Object {
body.complete();
return;
}
else if (read < 0) {
warning("transfer_more_bytes: stream.read failed");
return;
}
bytes.length = (int)read;
body.append_buffer(new Soup.Buffer.take(bytes));
}
@ -101,6 +105,10 @@ public class HttpFileSender : FileSender, Object {
put_message.request_body.set_accumulate(false);
put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
put_message.wrote_body_data.connect((chunk) => {
debug(@"wrote_body_data: chunk.length=$(chunk.length)");
progress(chunk.length);
});
#endif
foreach (var entry in file_send_data.headers.entries) {
put_message.request_headers.append(entry.key, entry.value);

View File

@ -41,6 +41,14 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidge
add(widget);
widget.visible = true;
} else {
var dialog = new Gtk.MessageDialog(null, MODAL, WARNING, OK, null);
dialog.text = "Could not create GTK video sink";
dialog.secondary_text = "Please install package gstreamer1.0-gtk3 or equivalent.";
dialog.response.connect((response_id) => {
dialog.close();
});
dialog.run();
warning("Could not create GTK video sink. Won't display videos.");
}
size_allocate.connect_after(after_size_allocate);