[fractal] Add typing notifications
- From: Christopher Davis <christopherdavis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] Add typing notifications
- Date: Thu, 7 Mar 2019 08:40:19 +0000 (UTC)
commit 48c8013be424db5178e68232e9dd68f7725b8d81
Author: Rasmus Rendal <rasmus rend al>
Date: Thu Mar 7 08:40:03 2019 +0000
Add typing notifications
Add handler for incoming typing notifications in the sync loop
Add an appop handler for typing notifications to the frontend
Feature added for #305
fractal-gtk/res/app.css | 7 +++++
fractal-gtk/src/app/backend_loop.rs | 2 +-
fractal-gtk/src/appop/room.rs | 46 ++++++++++++++++++++++++++++++--
fractal-gtk/src/widgets/room_history.rs | 4 +++
fractal-gtk/src/widgets/scroll_widget.rs | 30 ++++++++++++++++++++-
fractal-matrix-api/src/backend/sync.rs | 37 ++++++++++++++++++++++++-
fractal-matrix-api/src/backend/types.rs | 2 +-
fractal-matrix-api/src/model/room.rs | 1 +
8 files changed, 123 insertions(+), 6 deletions(-)
---
diff --git a/fractal-gtk/res/app.css b/fractal-gtk/res/app.css
index 02a8ec1c..b41e8bcf 100644
--- a/fractal-gtk/res/app.css
+++ b/fractal-gtk/res/app.css
@@ -310,3 +310,10 @@ stack.titlebar:not(headerbar) > box > separator {
.scrollarea-top-border {
border-top: 1px solid @borders;
}
+
+.typing_label {
+ margin: 6px;
+ margin-left: 78px;
+ margin-bottom: 8px;
+ color: @theme_selected_bg_color;
+}
diff --git a/fractal-gtk/src/app/backend_loop.rs b/fractal-gtk/src/app/backend_loop.rs
index 999910e1..4b70e155 100644
--- a/fractal-gtk/src/app/backend_loop.rs
+++ b/fractal-gtk/src/app/backend_loop.rs
@@ -112,7 +112,7 @@ pub fn backend_loop(rx: Receiver<BKResponse>) {
APPOP!(set_active_room_by_id, (room_id));
}
}
- Ok(BKResponse::NewRooms(rooms)) => {
+ Ok(BKResponse::UpdateRooms(rooms)) => {
let clear_room_list = false;
APPOP!(set_rooms, (rooms, clear_room_list));
}
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index f055f601..0004f63f 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -1,4 +1,4 @@
-use crate::i18n::{i18n, i18n_k};
+use crate::i18n::{i18n, i18n_k, ni18n_f};
use log::{error, warn};
use std::fs::remove_file;
use std::os::unix::fs;
@@ -18,13 +18,15 @@ use crate::actions::AppState;
use crate::cache;
use crate::widgets;
-use crate::types::{Room, RoomMembership, RoomTag};
+use crate::types::{Member, Room, RoomMembership, RoomTag};
use crate::util::markup_text;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
+use glib::functions::markup_escape_text;
+
pub struct Force(pub bool);
impl AppOp {
@@ -49,6 +51,14 @@ impl AppOp {
}
} else if self.rooms.contains_key(&room.id) {
// TODO: update the existing rooms
+ let update_room = self.rooms.get_mut(&room.id).unwrap();
+ let typing_users: Vec<Member> = room
+ .typing_users
+ .iter()
+ .map(|u| update_room.members.get(&u.uid).unwrap_or(&u).to_owned())
+ .collect();
+ update_room.typing_users = typing_users;
+ self.update_typing_notification();
} else {
// Request all joined members for each new room
self.backend
@@ -178,6 +188,7 @@ impl AppOp {
self.active_room = Some(active_room);
/* Mark the new active room as read */
self.mark_last_message_as_read(Force(false));
+ self.update_typing_notification();
}
pub fn really_leave_active_room(&mut self) {
@@ -514,4 +525,35 @@ impl AppOp {
self.backend.send(BKCommand::GetRoomAvatar(roomid)).unwrap();
}
+
+ pub fn update_typing_notification(&mut self) {
+ if let Some(active_room) = &self
+ .rooms
+ .get(&self.active_room.clone().unwrap_or_default())
+ {
+ if let Some(ref mut history) = self.history {
+ let typing_users = &active_room.typing_users;
+ if typing_users.len() == 0 {
+ history.typing_notification("");
+ } else if typing_users.len() > 2 {
+ history.typing_notification(&i18n("Several users are typing…"));
+ } else {
+ let typing_string = ni18n_f(
+ "<b>{}</b> is typing…",
+ "<b>{}</b> and {} are typing…",
+ typing_users.len() as u32,
+ typing_users
+ .iter()
+ .map(|user| markup_escape_text(&user.get_alias()))
+ .collect::<Vec<String>>()
+ .iter()
+ .map(std::ops::Deref::deref)
+ .collect::<Vec<&str>>()
+ .as_slice(),
+ );
+ history.typing_notification(&typing_string);
+ }
+ }
+ }
+ }
}
diff --git a/fractal-gtk/src/widgets/room_history.rs b/fractal-gtk/src/widgets/room_history.rs
index bc40e24f..9ab8a6c9 100644
--- a/fractal-gtk/src/widgets/room_history.rs
+++ b/fractal-gtk/src/widgets/room_history.rs
@@ -271,6 +271,10 @@ impl RoomHistory {
None
}
+
+ pub fn typing_notification(&mut self, typing_str: &str) {
+ self.rows.borrow().view.typing_notification(typing_str);
+ }
}
/* This function creates the content for a Row based on the conntent of msg */
diff --git a/fractal-gtk/src/widgets/scroll_widget.rs b/fractal-gtk/src/widgets/scroll_widget.rs
index 8fac41fe..8fd53639 100644
--- a/fractal-gtk/src/widgets/scroll_widget.rs
+++ b/fractal-gtk/src/widgets/scroll_widget.rs
@@ -35,6 +35,7 @@ pub struct Widgets {
btn_revealer: gtk::Revealer,
listbox: gtk::ListBox,
spinner: gtk::Spinner,
+ typing_label: gtk::Label,
}
impl Widgets {
@@ -67,7 +68,24 @@ impl Widgets {
let column = column.downcast::<gtk::Container>().unwrap();
column.set_hexpand(true);
column.set_vexpand(true);
- column.add(&messages);
+
+ let typing_label = gtk::Label::new(None);
+ typing_label.show();
+ typing_label
+ .get_style_context()
+ .unwrap()
+ .add_class("typing_label");
+ typing_label.set_xalign(0.0);
+ typing_label.set_property_wrap(true);
+ typing_label.set_property_wrap_mode(pango::WrapMode::WordChar);
+ typing_label.set_visible(false);
+ typing_label.set_use_markup(true);
+
+ let column_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
+ column_box.add(&messages);
+ column_box.add(&typing_label);
+ column_box.show();
+ column.add(&column_box);
column.show();
messages
@@ -93,6 +111,7 @@ impl Widgets {
btn_revealer,
listbox: messages,
spinner,
+ typing_label,
}
}
}
@@ -246,6 +265,15 @@ impl ScrollWidget {
self.request_sent.set(false);
self.widgets.spinner.stop();
}
+
+ pub fn typing_notification(&self, typing_str: &str) {
+ if typing_str.len() == 0 {
+ self.widgets.typing_label.set_visible(false);
+ } else {
+ self.widgets.typing_label.set_visible(true);
+ self.widgets.typing_label.set_markup(typing_str);
+ }
+ }
}
/* Functions to animate the scroll */
diff --git a/fractal-matrix-api/src/backend/sync.rs b/fractal-matrix-api/src/backend/sync.rs
index 23c8d914..f76ce26c 100644
--- a/fractal-matrix-api/src/backend/sync.rs
+++ b/fractal-matrix-api/src/backend/sync.rs
@@ -5,16 +5,21 @@ use crate::globals;
use crate::types::Event;
use crate::types::EventFilter;
use crate::types::Filter;
+use crate::types::Member;
use crate::types::Message;
use crate::types::Room;
use crate::types::RoomEventFilter;
use crate::types::RoomFilter;
+use crate::types::RoomMembership;
+use crate::types::RoomTag;
use crate::types::SyncResponse;
use crate::types::UnreadNotificationsCount;
use crate::util::json_q;
use crate::util::parse_m_direct;
+
use log::error;
use serde_json::json;
+use serde_json::value::from_value;
use serde_json::Value as JsonValue;
use std::{thread, time};
@@ -92,7 +97,7 @@ pub fn sync(bk: &Backend, new_since: Option<String>, initial: bool) -> Result<()
// New rooms
let rs = Room::from_sync_response(&response, &userid, &baseu);
- tx.send(BKResponse::NewRooms(rs)).unwrap();
+ tx.send(BKResponse::UpdateRooms(rs)).unwrap();
// Message events
let msgs = join
@@ -114,6 +119,36 @@ pub fn sync(bk: &Backend, new_since: Option<String>, initial: bool) -> Result<()
.unwrap();
}
+ // Typing notifications
+ let rooms: Vec<Room> = join
+ .iter()
+ .map(|(k, room)| {
+ let ephemerals = &room.ephemeral.events;
+ let mut typing_room: Room =
+ Room::new(k.clone(), RoomMembership::Joined(RoomTag::None));
+ let mut typing: Vec<Member> = Vec::new();
+ for event in ephemerals.iter() {
+ if let Some(typing_users) = event
+ .get("content")
+ .and_then(|x| x.get("user_ids"))
+ .and_then(|x| x.as_array())
+ {
+ for user in typing_users {
+ let user: String = from_value(user.to_owned()).unwrap();
+ typing.push(Member {
+ uid: user,
+ alias: None,
+ avatar: None,
+ });
+ }
+ }
+ }
+ typing_room.typing_users = typing;
+ typing_room
+ })
+ .collect();
+ tx.send(BKResponse::UpdateRooms(rooms)).unwrap();
+
// Other events
join.iter()
.flat_map(|(k, room)| {
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index 9f72a41d..f9cda612 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -105,7 +105,7 @@ pub enum BKResponse {
SetUserAvatar(String),
Sync(String),
Rooms(Vec<Room>, Option<Room>),
- NewRooms(Vec<Room>),
+ UpdateRooms(Vec<Room>),
RoomDetail(String, String, String),
RoomAvatar(String, Option<Url>),
NewRoomAvatar(String),
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index b2ff1f1c..3f4fd955 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -81,6 +81,7 @@ pub struct Room {
pub membership: RoomMembership,
pub direct: bool,
pub prev_batch: Option<String>,
+ pub typing_users: Vec<Member>,
/// Hashmap with the room users power levels
/// the key will be the userid and the value will be the level
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]