From bc3738aba1d092453b40acda73eb4a336171002f Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 22 Apr 2023 16:33:45 +0200 Subject: [PATCH 01/10] Fix GitHub CI build-flatpak --- .github/workflows/build.yml | 3 ++- im.dino.Dino.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a04b0f33..1f3ecc87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,4 +36,5 @@ jobs: fetch-depth: 0 - uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1 with: - manifest-path: im.dino.Dino.json \ No newline at end of file + manifest-path: im.dino.Dino.json + build-bundle: false diff --git a/im.dino.Dino.json b/im.dino.Dino.json index b2344c61..2df3460c 100644 --- a/im.dino.Dino.json +++ b/im.dino.Dino.json @@ -9,9 +9,10 @@ "--socket=fallback-x11", "--socket=wayland", "--socket=pulseaudio", + "--socket=pipewire", "--socket=gpg-agent", "--share=network", - "--device=all", + "--device=dri", "--talk-name=org.freedesktop.Notifications" ], "modules": [ From cad066628a9dc53d18288478ee74f5b3be28c7ee Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 21 Apr 2023 17:15:37 +0200 Subject: [PATCH 02/10] Build: Adjust to never build with libsignal-protocol-c --- .github/workflows/build.yml | 2 +- .gitmodules | 4 -- configure | 53 +------------------- plugins/signal-protocol/CMakeLists.txt | 52 ++----------------- plugins/signal-protocol/libsignal-protocol-c | 1 - 5 files changed, 6 insertions(+), 106 deletions(-) delete mode 100644 .gitmodules delete mode 160000 plugins/signal-protocol/libsignal-protocol-c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f3ecc87..e3a6a2b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: fetch-depth: 0 - run: sudo apt-get update - run: sudo apt-get remove libunwind-14-dev - - run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev + - run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev libsignal-protocol-c-dev - run: ./configure --with-tests --with-libsignal-in-tree - run: make - run: build/xmpp-vala-test diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b5480660..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "libsignal-protocol-c"] - path = plugins/signal-protocol/libsignal-protocol-c - url = https://github.com/WhisperSystems/libsignal-protocol-c.git - branch = v2.3.3 diff --git a/configure b/configure index 30b0bed5..b404fb17 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #!/bin/sh OPTS=`getopt -o "h" --long \ -help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,with-libsoup3,\ +help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsoup3,\ enable-plugin:,disable-plugin:,\ prefix:,program-prefix:,exec-prefix:,lib-suffix:,\ bindir:,libdir:,includedir:,datadir:,\ @@ -15,13 +15,11 @@ eval set -- "$OPTS" PREFIX=${PREFIX:-/usr/local} ENABLED_PLUGINS= DISABLED_PLUGINS= -BUILD_LIBSIGNAL_IN_TREE= BUILD_TESTS= BUILD_TYPE=Debug DISABLE_FAST_VAPI= LIB_SUFFIX= NO_DEBUG= -FETCH_ONLY= USE_SOUP3= EXEC_PREFIX= @@ -55,8 +53,6 @@ Configuration: without network access later and exit. --no-debug Build without debug symbols --release Configure to build an optimized release version - --with-libsignal-in-tree Build libsignal-protocol-c in tree and link it - statically. --with-libsoup3 Build with libsoup-3.0 --with-tests Also build tests. @@ -112,11 +108,9 @@ while true; do --valac ) VALA_EXECUTABLE="$2"; shift; shift ;; --valac-flags ) VALAC_FLAGS="$2"; shift; shift ;; --lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;; - --with-libsignal-in-tree ) BUILD_LIBSIGNAL_IN_TREE=yes; shift ;; --with-libsoup3 ) USE_SOUP3=yes; shift ;; --disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;; --no-debug ) NO_DEBUG=yes; shift ;; - --fetch-only ) FETCH_ONLY=yes; shift ;; --release ) BUILD_TYPE=RelWithDebInfo; shift ;; --with-tests ) BUILD_TESTS=yes; shift ;; # Autotools paths @@ -141,50 +135,6 @@ while true; do esac done -if [ "$BUILD_LIBSIGNAL_IN_TREE" = "yes" ] || [ "$FETCH_ONLY" = "yes" ]; then - if [ -d ".git" ]; then - git submodule update --init 2>/dev/null - else - tmp=0 - for i in $(cat .gitmodules | grep -n submodule | awk -F ':' '{print $1}') $(wc -l .gitmodules | awk '{print $1}'); do - if ! [ $tmp -eq 0 ]; then - name=$(cat .gitmodules | head -n $tmp | tail -n 1 | awk -F '"' '{print $2}') - def=$(cat .gitmodules | head -n $i | tail -n $(expr "$i" - "$tmp") | awk -F ' ' '{print $1 $2 $3}') - path=$(echo "$def" | grep '^path=' | awk -F '=' '{print $2}') - url=$(echo "$def" | grep '^url=' | awk -F '=' '{print $2}') - branch=$(echo "$def" | grep '^branch=' | awk -F '=' '{print $2}') - - if ! ls "$path"/* >/dev/null 2>/dev/null; then - git=$(which git) - if ! [ $? -eq 0 ] || ! [ -x $git ]; then - echo "Failed retrieving missing files" - exit 5 - fi - res=$(git clone "$url" "$path" 2>&1) - if ! [ $? -eq 0 ] || ! [ -d $path ]; then - echo "Failed retrieving missing files: $res" - exit 5 - fi - if [ -n "$branch" ]; then - olddir="$(pwd)" - cd "$path" - res=$(git checkout "$branch" 2>&1) - if ! [ $? -eq 0 ]; then - echo "Failed retrieving missing files: $res" - exit 5 - fi - cd "$olddir" - fi - echo "Submodule path '$path': checked out '$branch' (via git clone)" - fi - fi - tmp=$i - done - fi -fi - -if [ "$FETCH_ONLY" = "yes" ]; then exit 0; fi - if [ ! -x "$(which cmake 2>/dev/null)" ] then echo "-!- CMake required." @@ -258,7 +208,6 @@ cmake -G "$cmake_type" \ -DENABLED_PLUGINS="$ENABLED_PLUGINS" \ -DDISABLED_PLUGINS="$DISABLED_PLUGINS" \ -DBUILD_TESTS="$BUILD_TESTS" \ - -DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \ -DUSE_SOUP3="$USE_SOUP3" \ -DVALA_EXECUTABLE="$VALAC" \ -DCMAKE_VALA_FLAGS="$VALACFLAGS" \ diff --git a/plugins/signal-protocol/CMakeLists.txt b/plugins/signal-protocol/CMakeLists.txt index ea7dc9bb..b3cfae9d 100644 --- a/plugins/signal-protocol/CMakeLists.txt +++ b/plugins/signal-protocol/CMakeLists.txt @@ -28,54 +28,10 @@ GENERATE_HEADER set(C_HEADERS_SRC "") set(C_HEADERS_TARGET "") -if(NOT BUILD_LIBSIGNAL_IN_TREE) - # libsignal-protocol-c has a history of breaking compatibility on the patch level - # we'll have to check compatibility for every new release - # distro maintainers may update this dependency after compatibility tests - find_package(SignalProtocol 2.3.2 REQUIRED) -else() - add_subdirectory(libsignal-protocol-c EXCLUDE_FROM_ALL) - set_property(TARGET curve25519 PROPERTY POSITION_INDEPENDENT_CODE ON) - set_property(TARGET protobuf-c PROPERTY POSITION_INDEPENDENT_CODE ON) - set_property(TARGET signal-protocol-c PROPERTY POSITION_INDEPENDENT_CODE ON) - - set(SIGNAL_PROTOCOL_C_HEADERS - signal_protocol.h - signal_protocol_types.h - curve.h - hkdf.h - ratchet.h - protocol.h - session_state.h - session_record.h - session_pre_key.h - session_builder.h - session_cipher.h - key_helper.h - sender_key.h - sender_key_state.h - sender_key_record.h - group_session_builder.h - group_cipher.h - fingerprint.h - device_consistency.h - ) - - file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/exports/signal") - - foreach(f ${SIGNAL_PROTOCOL_C_HEADERS}) - list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}") - list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal/${f}") - add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/signal/${f}" - COMMAND - cp "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" "${CMAKE_BINARY_DIR}/exports/signal/${f}" - DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/libsignal-protocol-c/src/${f}" - COMMENT - Copy header file signal/${f} - ) - endforeach(f) -endif() +# libsignal-protocol-c has a history of breaking compatibility on the patch level +# we'll have to check compatibility for every new release +# distro maintainers may update this dependency after compatibility tests +find_package(SignalProtocol 2.3.2 REQUIRED) list(APPEND C_HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/signal_helper.h") list(APPEND C_HEADERS_TARGET "${CMAKE_BINARY_DIR}/exports/signal_helper.h") diff --git a/plugins/signal-protocol/libsignal-protocol-c b/plugins/signal-protocol/libsignal-protocol-c deleted file mode 160000 index 3a83a4f4..00000000 --- a/plugins/signal-protocol/libsignal-protocol-c +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a83a4f4ed2302ff6e68ab569c88793b50c22d28 From dbb8abc1178720020ca872361e23b2e941b874f4 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 21 Apr 2023 17:38:15 +0200 Subject: [PATCH 03/10] Fix video for cameras with rotated image --- plugins/rtp/src/device.vala | 28 ++++++++-- plugins/rtp/src/plugin.vala | 4 +- plugins/rtp/src/stream.vala | 91 ++++++++++++++++++++++++++++--- plugins/rtp/src/video_widget.vala | 14 ++++- 4 files changed, 122 insertions(+), 15 deletions(-) diff --git a/plugins/rtp/src/device.vala b/plugins/rtp/src/device.vala index 1db8c996..7fee7307 100644 --- a/plugins/rtp/src/device.vala +++ b/plugins/rtp/src/device.vala @@ -215,7 +215,12 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { } if (new_width == active_caps_width) return; int new_height = device_caps_height * new_width / device_caps_width; - Gst.Caps new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null); + Gst.Caps new_caps; + if (device_caps_framerate_den != 0) { + new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null); + } else { + new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, null); + } double required_bitrate = get_target_bitrate(new_caps); debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate); if (bitrate < required_bitrate && new_width > active_caps_width) return; @@ -347,7 +352,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { if (media == "audio") { return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1"); } else if (media == "video" && device.caps.get_size() > 0) { - int best_index = 0; + int best_index = -1; Value? best_fraction = null; int best_fps = 0; int best_width = 0; @@ -390,10 +395,25 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { best_fraction = best_fraction_now; } } + if (best_index == -1) { + // No caps in first round, try without framerate + for (int i = 0; i < device.caps.get_size(); i++) { + unowned Gst.Structure? that = device.caps.get_structure(i); + if (!that.has_name("video/x-raw")) continue; + int width = 0, height = 0; + if (!that.has_field("width") || !that.get_int("width", out width)) continue; + if (!that.has_field("height") || !that.get_int("height", out height)) continue; + if (best_width < width || best_width == width && best_height < height) { + best_width = width; + best_height = height; + best_index = i; + } + } + } Gst.Caps res = caps_copy_nth(device.caps, best_index); unowned Gst.Structure? that = res.get_structure(0); - Value framerate = that.get_value("framerate"); - if (framerate.type() == typeof(Gst.ValueList)) { + Value? framerate = that.get_value("framerate"); + if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) { that.set_value("framerate", best_fraction); } debug("Selected caps %s", res.to_string()); diff --git a/plugins/rtp/src/plugin.vala b/plugins/rtp/src/plugin.vala index aefe41ff..98b9717d 100644 --- a/plugins/rtp/src/plugin.vala +++ b/plugins/rtp/src/plugin.vala @@ -426,11 +426,11 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { if (media == "video") { // Pick best FPS - int max_fps = 0; + int max_fps = -1; Device? max_fps_device = null; foreach (Device device in devices) { int fps = get_max_fps(device); - if (fps > max_fps) { + if (fps > max_fps || max_fps_device == null) { max_fps = fps; max_fps_device = device; } diff --git a/plugins/rtp/src/stream.vala b/plugins/rtp/src/stream.vala index abdda776..94549856 100644 --- a/plugins/rtp/src/stream.vala +++ b/plugins/rtp/src/stream.vala @@ -102,6 +102,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { send_rtp.drop = true; send_rtp.wait_on_eos = false; send_rtp.new_sample.connect(on_new_sample); +#if GST_1_20 + send_rtp.new_serialized_event.connect(on_new_event); +#endif send_rtp.connect("signal::eos", on_eos_static, this); pipe.add(send_rtp); @@ -294,6 +297,60 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { } } + bool flip = false; + uint8 rotation = 0; +#if GST_1_20 + private bool on_new_event(Gst.App.Sink sink) { + if (sink == null || sink != send_rtp) { + return false; + } + Gst.MiniObject obj = sink.try_pull_object(0); + if (obj.type == typeof(Gst.Event)) { + unowned Gst.TagList tags; + if (((Gst.Event)obj).type == Gst.EventType.TAG) { + ((Gst.Event)obj).parse_tag(out tags); + Gst.Video.OrientationMethod orientation_method; + Gst.Video.Orientation.from_tag(tags, out orientation_method); + switch (orientation_method) { + case Gst.Video.OrientationMethod.IDENTITY: + case Gst.Video.OrientationMethod.VERT: + default: + rotation = 0; + break; + case Gst.Video.OrientationMethod.@90R: + case Gst.Video.OrientationMethod.UL_LR: + rotation = 1; + break; + case Gst.Video.OrientationMethod.@180: + case Gst.Video.OrientationMethod.HORIZ: + rotation = 2; + break; + case Gst.Video.OrientationMethod.@90L: + case Gst.Video.OrientationMethod.UR_LL: + rotation = 3; + break; + } + switch (orientation_method) { + case Gst.Video.OrientationMethod.IDENTITY: + case Gst.Video.OrientationMethod.@90R: + case Gst.Video.OrientationMethod.@180: + case Gst.Video.OrientationMethod.@90L: + default: + flip = false; + break; + case Gst.Video.OrientationMethod.VERT: + case Gst.Video.OrientationMethod.UL_LR: + case Gst.Video.OrientationMethod.HORIZ: + case Gst.Video.OrientationMethod.UR_LL: + flip = true; + break; + } + } + } + return false; + } +#endif + private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) { if (sink == null) { debug("Sink is null"); @@ -323,6 +380,24 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { #endif } +#if GST_1_20 + if (sink == send_rtp) { + Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation"); + if (ext != null) { + buffer = (Gst.Buffer) buffer.make_writable(); + Gst.RTP.Buffer rtp_buffer; + if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.WRITE, out rtp_buffer)) { + uint8[] extension_data = new uint8[1]; + bool camera = false; + extension_data[0] = extension_data[0] | (rotation & 0x3); + if (flip) extension_data[0] = extension_data[0] | 0x4; + if (camera) extension_data[0] = extension_data[0] | 0x8; + rtp_buffer.add_extension_onebyte_header(ext.id, extension_data); + } + } + } +#endif + prepare_local_crypto(); uint8[] data; @@ -489,8 +564,8 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { } } - private uint16 previous_video_orientation_degree = uint16.MAX; - public signal void video_orientation_changed(uint16 degree); + private uint16 previous_incoming_video_orientation_degree = uint16.MAX; + public signal void incoming_video_orientation_changed(uint16 degree); public override void on_recv_rtp_data(Bytes bytes) { if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) { @@ -545,9 +620,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { case 2: rotation_degree = 180; break; case 3: rotation_degree = 270; break; } - if (rotation_degree != previous_video_orientation_degree) { - video_orientation_changed(rotation_degree); - previous_video_orientation_degree = rotation_degree; + if (rotation_degree != previous_incoming_video_orientation_degree) { + incoming_video_orientation_changed(rotation_degree); + previous_incoming_video_orientation_degree = rotation_degree; } } } @@ -723,7 +798,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream { private Gee.List outputs = new ArrayList(); private Gst.Element output_tee; private Gst.Element rotate; - private ulong video_orientation_changed_handler; + private ulong incoming_video_orientation_changed_handler; public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(plugin, content); @@ -731,7 +806,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream { } public override void create() { - video_orientation_changed_handler = video_orientation_changed.connect(on_video_orientation_changed); + incoming_video_orientation_changed_handler = incoming_video_orientation_changed.connect(on_video_orientation_changed); plugin.pause(); rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid"); pipe.add(rotate); @@ -780,7 +855,7 @@ public class Dino.Plugins.Rtp.VideoStream : Stream { output_tee.set_state(Gst.State.NULL); pipe.remove(output_tee); output_tee = null; - disconnect(video_orientation_changed_handler); + disconnect(incoming_video_orientation_changed_handler); } public override void add_output(Gst.Element element, Xmpp.Jid? participant) { diff --git a/plugins/rtp/src/video_widget.vala b/plugins/rtp/src/video_widget.vala index 0d66476b..20123c68 100644 --- a/plugins/rtp/src/video_widget.vala +++ b/plugins/rtp/src/video_widget.vala @@ -227,9 +227,21 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWi if (connected_device == null) return; plugin.pause(); pipe.add(sink); +#if GST_1_20 + prepare = Gst.parse_bin_from_description(@"videoflip video-direction=auto name=video_widget_$(id)_orientation ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); +#else prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); +#endif prepare.name = @"video_widget_$(id)_prepare"; - prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); +#if GST_1_20 + if (prepare is Gst.Bin) { + ((Gst.Bin) prepare).get_by_name(@"video_widget_$(id)_flip").get_static_pad("sink").notify["caps"].connect(input_caps_changed); + } else { +#endif + prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); +#if GST_1_20 + } +#endif pipe.add(prepare); connected_device_element = connected_device.link_source(); connected_device_element.link(prepare); From 5815e757b710aa5d9f6be7c5310ab7a9f94820f6 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Fri, 21 Apr 2023 17:40:19 +0200 Subject: [PATCH 04/10] Fix call window controlls hiding --- main/src/ui/call_window/call_window.vala | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/main/src/ui/call_window/call_window.vala b/main/src/ui/call_window/call_window.vala index cf89d8e8..14b67449 100644 --- a/main/src/ui/call_window/call_window.vala +++ b/main/src/ui/call_window/call_window.vala @@ -22,7 +22,12 @@ namespace Dino.Ui { private HashMap participant_widgets = new HashMap(); private ArrayList participants = new ArrayList(); + private EventControllerFocus this_focus_events = new EventControllerFocus(); + private GestureClick this_gesture_events = new GestureClick() { touch_only=true, propagation_phase=Gtk.PropagationPhase.CAPTURE }; private EventControllerMotion this_motion_events = new EventControllerMotion(); + private double latest_motion_x = -1; + private double latest_motion_y = -1; + private const double MOTION_RELEVANCE_THRESHOLD = 2; private int own_video_width = 150; private int own_video_height = 100; @@ -54,9 +59,20 @@ namespace Dino.Ui { this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE); ((Widget) this).add_controller(this_motion_events); - this_motion_events.motion.connect(reveal_control_elements); - this_motion_events.enter.connect(reveal_control_elements); - this_motion_events.leave.connect(reveal_control_elements); + this_motion_events.motion.connect((x, y) => { + if ((latest_motion_x - x).abs() <= MOTION_RELEVANCE_THRESHOLD && (latest_motion_y - y).abs() <= MOTION_RELEVANCE_THRESHOLD) return; + + latest_motion_x = x; + latest_motion_y = y; + reveal_control_elements(); + }); + + ((Widget) this).add_controller(this_focus_events); + this_focus_events.enter.connect(reveal_control_elements); + this_focus_events.leave.connect(reveal_control_elements); + + ((Widget) this).add_controller(this_gesture_events); + this_gesture_events.pressed.connect_after(reveal_control_elements); this.notify["default-width"].connect(reveal_control_elements); this.notify["default-height"].connect(reveal_control_elements); From 83476d1cadf7c2bddc79c2e44b6b4e28192f8f18 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 22 Apr 2023 17:18:46 +0200 Subject: [PATCH 05/10] Fix Flatpak pipewire socket access --- im.dino.Dino.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/im.dino.Dino.json b/im.dino.Dino.json index 2df3460c..c754c88e 100644 --- a/im.dino.Dino.json +++ b/im.dino.Dino.json @@ -9,8 +9,8 @@ "--socket=fallback-x11", "--socket=wayland", "--socket=pulseaudio", - "--socket=pipewire", "--socket=gpg-agent", + "--filesystem=xdg-run/pipewire-0", "--share=network", "--device=dri", "--talk-name=org.freedesktop.Notifications" From 03e367ecb88fe142a6244b397ca32531160ccbb0 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sat, 22 Apr 2023 19:47:32 +0200 Subject: [PATCH 06/10] Fix call window styling --- main/data/style.css | 8 ++------ main/src/ui/call_window/participant_widget.vala | 3 --- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/main/data/style.css b/main/data/style.css index deac24fe..af1c58fa 100644 --- a/main/data/style.css +++ b/main/data/style.css @@ -396,17 +396,13 @@ box.dino-input-error .chat-input-status.input-status-highlight-once { text-shadow: 0 0 2px black; } -.dino-call-window .call-header-background { +.dino-call-window .participant-header-bar { background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0)); border: 0; border-radius: 0; } -.dino-call-window .participant-header-bar button { - background: none; -} - -.dino-call-window .participant-header-bar button:hover { +.dino-call-window .participant-header-bar button:hover:not(.close) { background: rgba(255,255,255,0.15); border-color: rgba(255,255,255,0); box-shadow: none; diff --git a/main/src/ui/call_window/participant_widget.vala b/main/src/ui/call_window/participant_widget.vala index 8ec1f5ea..bed64c30 100644 --- a/main/src/ui/call_window/participant_widget.vala +++ b/main/src/ui/call_window/participant_widget.vala @@ -74,14 +74,11 @@ namespace Dino.Ui { header_bar.show_title_buttons = is_highest_row; if (is_highest_row) { - header_bar.add_css_class("call-header-background"); Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); if (gtk_settings != null) { string[] buttons = gtk_settings.gtk_decoration_layout.split(":"); header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : ""); } - } else { - header_bar.remove_css_class("call-header-background"); } reveal_or_hide_controls(); } From 6e60cfcbbe6a1310acb23f975f227a176aa83006 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 22 Apr 2023 20:07:09 +0200 Subject: [PATCH 07/10] Fix empty alias being handled different than none --- libdino/src/util/display_name.vala | 1 + 1 file changed, 1 insertion(+) diff --git a/libdino/src/util/display_name.vala b/libdino/src/util/display_name.vala index b591eb0a..42b30600 100644 --- a/libdino/src/util/display_name.vala +++ b/libdino/src/util/display_name.vala @@ -34,6 +34,7 @@ namespace Dino { if (self_word != null && (account.alias == null || account.alias.length == 0)) { return self_word; } + if (account.alias != null && account.alias.length == 0) return null; return account.alias; } Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid); From 2b9a0ccf7ea262b40eaf1513a54fbc2b326673f3 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sun, 23 Apr 2023 11:16:27 +0200 Subject: [PATCH 08/10] Fix crash on NS_URI call when own server has no MAM; drop broken mam:1 "support" fixes #1405 --- libdino/src/service/history_sync.vala | 8 +-- libdino/src/service/message_processor.vala | 14 +++-- libdino/src/service/muc_manager.vala | 2 +- libdino/src/service/reactions.vala | 2 +- .../0313_2_message_archive_management.vala | 6 +- .../xep/0313_message_archive_management.vala | 55 +++++++------------ 6 files changed, 37 insertions(+), 50 deletions(-) diff --git a/libdino/src/service/history_sync.vala b/libdino/src/service/history_sync.vala index 2444a133..0c0571bb 100644 --- a/libdino/src/service/history_sync.vala +++ b/libdino/src/service/history_sync.vala @@ -90,11 +90,9 @@ public class Dino.HistorySync { if (!is_muc_mam && !from_our_server) return; // Get the server time of the message and store it in `mam_times` - Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null; - if (mam_flag == null) return; - string? id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", "id"); + string? id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", "id"); if (id == null) return; - StanzaNode? delay_node = message.stanza.get_deep_subnode(mam_flag.ns_ver + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); + StanzaNode? delay_node = message.stanza.get_deep_subnode(Xmpp.MessageArchiveManagement.NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); if (delay_node == null) { warning("MAM result did not contain delayed time %s", message.stanza.to_string()); return; @@ -104,7 +102,7 @@ public class Dino.HistorySync { mam_times[account][id] = time; // Check if this is the target message - string? query_id = message.stanza.get_deep_attribute(mam_flag.ns_ver + ":result", mam_flag.ns_ver + ":queryid"); + string? query_id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", Xmpp.MessageArchiveManagement.NS_URI + ":queryid"); if (query_id != null && id == catchup_until_id[account]) { debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id); hitted_range[query_id] = -2; diff --git a/libdino/src/service/message_processor.vala b/libdino/src/service/message_processor.vala index 45f06a69..2af68e2e 100644 --- a/libdino/src/service/message_processor.vala +++ b/libdino/src/service/message_processor.vala @@ -170,19 +170,21 @@ public class MessageProcessor : StreamInteractionModule, Object { XmppStream? stream = stream_interactor.get_stream(account); Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); - Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null; EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); - if (mam_message_flag != null && mam_flag != null && mam_flag.ns_ver == Xmpp.MessageArchiveManagement.NS_URI_2 && mam_message_flag.mam_id != null) { - new_message.server_id = mam_message_flag.mam_id; + if (mam_message_flag != null && mam_message_flag.mam_id != null) { + bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI); + if (server_does_mam) { + new_message.server_id = mam_message_flag.mam_id; + } } else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || - (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2)); + (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid); } } else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || - (yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2)); + (yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid); } @@ -378,7 +380,7 @@ public class MessageProcessor : StreamInteractionModule, Object { public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null; XmppStream? stream = stream_interactor.get_stream(conversation.account); - Xmpp.MessageArchiveManagement.Flag? mam_flag = stream != null ? stream.get_flag(Xmpp.MessageArchiveManagement.Flag.IDENTITY) : null; + Xmpp.MessageArchiveManagement.Flag mam_flag = Xmpp.MessageArchiveManagement.Flag.get_flag(stream); if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) { conversation.account.mam_earliest_synced = message.local_time; } diff --git a/libdino/src/service/muc_manager.vala b/libdino/src/service/muc_manager.vala index f796c36f..e2e13435 100644 --- a/libdino/src/service/muc_manager.vala +++ b/libdino/src/service/muc_manager.vala @@ -72,7 +72,7 @@ public class MucManager : StreamInteractionModule, Object { bool receive_history = true; EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); - bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI_2); + bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI); if (can_do_mam) { receive_history = false; history_since = null; diff --git a/libdino/src/service/reactions.vala b/libdino/src/service/reactions.vala index 3621dab1..9277a00f 100644 --- a/libdino/src/service/reactions.vala +++ b/libdino/src/service/reactions.vala @@ -64,7 +64,7 @@ public class Dino.Reactions : StreamInteractionModule, Object { // The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids) var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || - (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI_2)); + (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (!server_supports_sid) return false; bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI); diff --git a/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala b/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala index 3a6d9259..dde7d9b0 100644 --- a/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala +++ b/xmpp-vala/src/module/xep/0313_2_message_archive_management.vala @@ -58,7 +58,7 @@ namespace Xmpp.MessageArchiveManagement.V2 { fields.add(field); } - return MessageArchiveManagement.create_base_query(stream, MessageArchiveManagement.NS_URI_2, mam_params.query_id, fields); + return MessageArchiveManagement.create_base_query(stream, mam_params.query_id, fields); } public async QueryResult query_archive(XmppStream stream, MamQueryParams mam_params, Cancellable? cancellable = null) { @@ -67,14 +67,14 @@ namespace Xmpp.MessageArchiveManagement.V2 { query_node.put_node(ResultSetManagement.create_set_rsm_node_before(mam_params.end_id)); } - return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable); + return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable); } public async QueryResult page_through_results(XmppStream stream, MamQueryParams mam_params, QueryResult prev_result, Cancellable? cancellable = null) { var query_node = create_base_query(stream, mam_params); query_node.put_node(ResultSetManagement.create_set_rsm_node_before(prev_result.first)); - return yield MessageArchiveManagement.query_archive(stream, MessageArchiveManagement.NS_URI_2, mam_params.mam_server, query_node, cancellable); + return yield MessageArchiveManagement.query_archive(stream, mam_params.mam_server, query_node, cancellable); } } diff --git a/xmpp-vala/src/module/xep/0313_message_archive_management.vala b/xmpp-vala/src/module/xep/0313_message_archive_management.vala index 2235e118..1a7789cb 100644 --- a/xmpp-vala/src/module/xep/0313_message_archive_management.vala +++ b/xmpp-vala/src/module/xep/0313_message_archive_management.vala @@ -4,8 +4,6 @@ using Xmpp.Xep; namespace Xmpp.MessageArchiveManagement { public const string NS_URI = "urn:xmpp:mam:2"; -public const string NS_URI_2 = "urn:xmpp:mam:2"; -public const string NS_URI_1 = "urn:xmpp:mam:1"; public class QueryResult { public bool error { get; set; default=false; } @@ -36,43 +34,33 @@ public class Module : XmppStreamModule { private async void query_availability(XmppStream stream) { Jid own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid; - - bool ver_2_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI); - if (ver_2_available) { - stream.add_flag(new Flag(NS_URI)); + bool mam_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI); + if (mam_available) { feature_available(stream); - return; - } - - bool ver_1_available = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, own_jid, NS_URI_1); - if (ver_1_available) { - stream.add_flag(new Flag(NS_URI_1)); - feature_available(stream); - return; } } } - internal StanzaNode create_base_query(XmppStream stream, string ns, string? queryid, Gee.List fields) { + internal StanzaNode create_base_query(XmppStream stream, string? queryid, Gee.List fields) { DataForms.DataForm data_form = new DataForms.DataForm(); DataForms.DataForm.HiddenField form_type_field = new DataForms.DataForm.HiddenField() { var="FORM_TYPE" }; - form_type_field.set_value_string(NS_VER(stream)); + form_type_field.set_value_string(NS_URI); data_form.add_field(form_type_field); foreach (var field in fields) { data_form.add_field(field); } - StanzaNode query_node = new StanzaNode.build("query", NS_VER(stream)).add_self_xmlns().put_node(data_form.get_submit_node()); + StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(data_form.get_submit_node()); query_node.put_attribute("queryid", queryid); return query_node; } - internal async QueryResult query_archive(XmppStream stream, string ns, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) { + internal async QueryResult query_archive(XmppStream stream, Jid? mam_server, StanzaNode query_node, Cancellable? cancellable = null) { var res = new QueryResult(); - Flag? flag = stream.get_flag(Flag.IDENTITY); + Flag flag = Flag.get_flag(stream); string? query_id = query_node.get_attribute("queryid"); if (flag == null || query_id == null) { res.error = true; return res; } flag.active_query_ids.add(query_id); @@ -83,7 +71,7 @@ public class Module : XmppStreamModule { Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq, Priority.LOW, cancellable); // Parse the response IQ into a QueryResult. - StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", ns); + StanzaNode? fin_node = result_iq.stanza.get_subnode("fin", NS_URI); if (fin_node == null) { res.malformed = true; return res; } StanzaNode? rsm_node = fin_node.get_subnode("set", Xmpp.ResultSetManagement.NS_URI); @@ -92,7 +80,7 @@ public class Module : XmppStreamModule { res.first = rsm_node.get_deep_string_content("first"); res.last = rsm_node.get_deep_string_content("last"); if ((res.first == null) != (res.last == null)) { res.malformed = true; return res; } - res.complete = fin_node.get_attribute_bool("complete", false, ns); + res.complete = fin_node.get_attribute_bool("complete", false, NS_URI); Idle.add(() => { flag.active_query_ids.remove(query_id); @@ -110,15 +98,14 @@ public class ReceivedPipelineListener : StanzaListener { public override string[] after_actions { get { return after_actions_const; } } public override async bool run(XmppStream stream, MessageStanza message) { - Flag? flag = stream.get_flag(Flag.IDENTITY); - if (flag == null) return false; + Flag flag = Flag.get_flag(stream); - StanzaNode? message_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message"); + StanzaNode? message_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", Xmpp.NS_URI + ":message"); if (message_node != null) { - StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_VER(stream) + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); + StanzaNode? forward_node = message.stanza.get_deep_subnode(NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); DateTime? datetime = DelayedDelivery.get_time_for_node(forward_node); - string? mam_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":id"); - string? query_id = message.stanza.get_deep_attribute(NS_VER(stream) + ":result", NS_VER(stream) + ":queryid"); + string? mam_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":id"); + string? query_id = message.stanza.get_deep_attribute(NS_URI + ":result", NS_URI + ":queryid"); if (query_id == null) { warning("Received MAM message without queryid from %s, ignoring", message.from.to_string()); @@ -154,10 +141,14 @@ public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "message_archive_management"); public bool cought_up { get; set; default=false; } public Gee.Set active_query_ids { get; set; default = new HashSet(); } - public string ns_ver; - public Flag(string ns_ver) { - this.ns_ver = ns_ver; + public static Flag get_flag(XmppStream stream) { + Flag? flag = stream.get_flag(Flag.IDENTITY); + if (flag == null) { + flag = new Flag(); + stream.add_flag(flag); + } + return flag; } public override string get_ns() { return NS_URI; } @@ -185,8 +176,4 @@ public class MessageFlag : Xmpp.MessageFlag { public override string get_id() { return ID; } } -private static string NS_VER(XmppStream stream) { - return stream.get_flag(Flag.IDENTITY).ns_ver; -} - } \ No newline at end of file From 10315a245d7ed23ac9c915ecb298a484050ccef1 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sun, 23 Apr 2023 11:48:29 +0200 Subject: [PATCH 09/10] Code cleanup: Remove left-over usages of mam_earliest_synced --- libdino/src/entity/account.vala | 5 ----- libdino/src/service/database.vala | 1 + libdino/src/service/message_processor.vala | 24 ---------------------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/libdino/src/entity/account.vala b/libdino/src/entity/account.vala index 2b7f2b04..51ceb8d1 100644 --- a/libdino/src/entity/account.vala +++ b/libdino/src/entity/account.vala @@ -18,7 +18,6 @@ public class Account : Object { public string? alias { get; set; } public bool enabled { get; set; default = false; } public string? roster_version { get; set; } - public DateTime mam_earliest_synced { get; set; default=new DateTime.from_unix_utc(0); } private Database? db; @@ -50,7 +49,6 @@ public class Account : Object { alias = row[db.account.alias]; enabled = row[db.account.enabled]; roster_version = row[db.account.roster_version]; - mam_earliest_synced = new DateTime.from_unix_utc(row[db.account.mam_earliest_synced]); notify.connect(on_update); } @@ -66,7 +64,6 @@ public class Account : Object { .value(db.account.alias, alias) .value(db.account.enabled, enabled) .value(db.account.roster_version, roster_version) - .value(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()) .perform(); notify.connect(on_update); @@ -106,8 +103,6 @@ public class Account : Object { update.set(db.account.enabled, enabled); break; case "roster-version": update.set(db.account.roster_version, roster_version); break; - case "mam-earliest-synced": - update.set(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()); break; } update.perform(); } diff --git a/libdino/src/service/database.vala b/libdino/src/service/database.vala index 6b3f5e6a..dc1d68f3 100644 --- a/libdino/src/service/database.vala +++ b/libdino/src/service/database.vala @@ -17,6 +17,7 @@ public class Database : Qlite.Database { public Column alias = new Column.Text("alias"); public Column enabled = new Column.BoolInt("enabled"); public Column roster_version = new Column.Text("roster_version") { min_version=2 }; + // no longer used. all usages already removed. remove db column at some point. public Column mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 }; internal AccountTable(Database db) { diff --git a/libdino/src/service/message_processor.vala b/libdino/src/service/message_processor.vala index 2af68e2e..04c7d1a5 100644 --- a/libdino/src/service/message_processor.vala +++ b/libdino/src/service/message_processor.vala @@ -38,7 +38,6 @@ public class MessageProcessor : StreamInteractionModule, Object { received_pipeline.connect(new FilterMessageListener()); received_pipeline.connect(new StoreMessageListener(this, stream_interactor)); received_pipeline.connect(new StoreContentItemListener(stream_interactor)); - received_pipeline.connect(new MamMessageListener(stream_interactor)); stream_interactor.account_added.connect(on_account_added); @@ -365,29 +364,6 @@ public class MessageProcessor : StreamInteractionModule, Object { } } - private class MamMessageListener : MessageListener { - - public string[] after_actions_const = new string[]{ "DEDUPLICATE" }; - public override string action_group { get { return "MAM_NODE"; } } - public override string[] after_actions { get { return after_actions_const; } } - - private StreamInteractor stream_interactor; - - public MamMessageListener(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - } - - public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { - bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null; - XmppStream? stream = stream_interactor.get_stream(conversation.account); - Xmpp.MessageArchiveManagement.Flag mam_flag = Xmpp.MessageArchiveManagement.Flag.get_flag(stream); - if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) { - conversation.account.mam_earliest_synced = message.local_time; - } - return false; - } - } - public Entities.Message create_out_message(string text, Conversation conversation) { Entities.Message message = new Entities.Message(text); message.type_ = Util.get_message_type_for_conversation(conversation); From ec6c24c2b41fc9de704323ce895b2dc81549ca5e Mon Sep 17 00:00:00 2001 From: Karim Malhas Date: Sat, 22 Apr 2023 13:20:24 +0200 Subject: [PATCH 10/10] Focus ChatInput textbox after selecting emoji After selecting an emoji, the emoji is inserted into the textbox, but focus remains on the emoji_button. This causes the EmojiChooser to be opened again if a user hits the Enter key directly, but text is inserted into the textbox if they continue to type. This commit just explicitely focuses on the textbox after an emoji has been selected. --- main/src/ui/chat_input/view.vala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/main/src/ui/chat_input/view.vala b/main/src/ui/chat_input/view.vala index e16b4085..96986b58 100644 --- a/main/src/ui/chat_input/view.vala +++ b/main/src/ui/chat_input/view.vala @@ -39,6 +39,8 @@ public class View : Box { chooser.emoji_picked.connect((emoji) => { chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length); }); + chooser.closed.connect(do_focus); + emoji_button.set_popover(chooser); file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file"));