[fractal/fractal-next] content: Show empty page and loading spinner
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] content: Show empty page and loading spinner
- Date: Mon, 12 Jul 2021 10:31:07 +0000 (UTC)
commit f9d656f154986f20909eb4fe63931926d8a80c51
Author: Julian Sparber <julian sparber net>
Date: Sun Jul 11 15:08:14 2021 +0200
content: Show empty page and loading spinner
.../resources/icons/scalable/status/empty-page.svg | 6 ++
data/resources/resources.gresource.xml | 1 +
data/resources/ui/content-room-history.ui | 58 ++++++++++++-------
data/resources/ui/content.ui | 32 +++++++++++
src/session/content/content.rs | 4 +-
src/session/content/room_history.rs | 65 +++++++++++++++++++---
src/session/room/timeline.rs | 33 ++++++++---
7 files changed, 160 insertions(+), 39 deletions(-)
---
diff --git a/data/resources/icons/scalable/status/empty-page.svg
b/data/resources/icons/scalable/status/empty-page.svg
new file mode 100644
index 00000000..17e38b34
--- /dev/null
+++ b/data/resources/icons/scalable/status/empty-page.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140" viewBox="0 0 37.042 37.042">
+ <g color="#000" fill="#bebebe">
+ <path d="M31.75 5.292v14.717c0 1.364-.138 2.933-1.158 4.3-1.019 1.366-2.896 2.218-5.457
2.15H14.387l-2.729 2.645h12.154l7.938 7.938v-7.938h2.646a2.64 2.64 0 0 0 2.646-2.646V7.938a2.64 2.64 0 0
0-2.646-2.646z" style="marker:none" overflow="visible" fill-opacity=".365"/>
+ <path d="M2.646 0A2.64 2.64 0 0 0 0 2.646v18.52a2.64 2.64 0 0 0 2.646
2.646h2.646v7.938l7.937-7.938h13.23a2.64 2.64 0 0 0 2.645-2.645V2.646A2.64 2.64 0 0 0 26.458 0z"
style="marker:none" overflow="visible"/>
+ </g>
+</svg>
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 2bde98bb..2a6d9825 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -32,6 +32,7 @@
<file compressed="true">style.css</file>
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>
+ <file preprocess="xml-stripblanks">icons/scalable/status/empty-page.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/explore-symbolic.svg</file>
</gresource>
</gresources>
diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui
index 7c5939cd..a9cf96e7 100644
--- a/data/resources/ui/content-room-history.ui
+++ b/data/resources/ui/content-room-history.ui
@@ -71,33 +71,49 @@
</object>
</child>
<child>
- <object class="GtkScrolledWindow" id="scrolled_window">
- <property name="vexpand">True</property>
- <property name="hscrollbar-policy">never</property>
- <style>
- <class name="room-history"/>
- </style>
- <property name="child">
- <object class="AdwClampScrollable">
+ <object class="GtkStack" id="stack">
+ <property name="transition-type">crossfade</property>
+ <child>
+ <object class="GtkSpinner" id="loading">
+ <property name="spinning">True</property>
+ <property name="valign">center</property>
+ <property name="halign">center</property>
<property name="vexpand">True</property>
- <property name="hexpand">True</property>
+ <style>
+ <class name="session-loading-spinner"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolled_window">
+ <property name="vexpand">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <style>
+ <class name="room-history"/>
+ </style>
<property name="child">
- <object class="GtkListView" id="listview">
- <style>
- <class name="navigation-sidebar"/>
- </style>
- <property name="factory">
- <object class="GtkBuilderListItemFactory">
- <property name="resource">/org/gnome/FractalNext/content-item.ui</property>
+ <object class="AdwClampScrollable">
+ <property name="vexpand">True</property>
+ <property name="hexpand">True</property>
+ <property name="child">
+ <object class="GtkListView" id="listview">
+ <style>
+ <class name="navigation-sidebar"/>
+ </style>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="resource">/org/gnome/FractalNext/content-item.ui</property>
+ </object>
+ </property>
+ <accessibility>
+ <property name="label" translatable="yes">Room History</property>
+ </accessibility>
</object>
</property>
- <accessibility>
- <property name="label" translatable="yes">Room History</property>
- </accessibility>
</object>
</property>
</object>
- </property>
+ </child>
</object>
</child>
<child>
@@ -131,7 +147,7 @@
</object>
</child>
<child>
- <object class="GtkScrolledWindow" id="scrolled_window">
+ <object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<property name="hexpand">True</property>
<property name="vscrollbar-policy">external</property>
diff --git a/data/resources/ui/content.ui b/data/resources/ui/content.ui
index 0560327a..560c8669 100644
--- a/data/resources/ui/content.ui
+++ b/data/resources/ui/content.ui
@@ -14,6 +14,38 @@
<child>
<object class="GtkStack" id="stack">
<property name="transition-type">crossfade</property>
+ <child>
+ <object class="GtkBox" id="empty_page">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="AdwHeaderBar" id="headerbar">
+ <property name="show-start-title-buttons" bind-source="Content" bind-property="compact"
bind-flags="sync-create"/>
+ <child type="start">
+ <object class="GtkRevealer">
+ <property name="transition-type">crossfade</property>
+ <property name="reveal-child" bind-source="Content" bind-property="compact"
bind-flags="sync-create"/>
+ <property name="child">
+ <object class="GtkButton" id="back">
+ <property name="icon-name">go-previous-symbolic</property>
+ <property name="action-name">content.go-back</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwStatusPage">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="icon-name">empty-page</property>
+ <property name="title" translatable="yes">No Room Selected</property>
+ <property name="description" translatable="yes">Join a room to start chatting.</property>
+ </object>
+ </child>
+ </object>
+ </child>
<child>
<object class="ContentRoomHistory" id="room_history">
<property name="compact" bind-source="Content" bind-property="compact"
bind-flags="sync-create"/>
diff --git a/src/session/content/content.rs b/src/session/content/content.rs
index 74b0eeeb..f2380d8a 100644
--- a/src/session/content/content.rs
+++ b/src/session/content/content.rs
@@ -29,6 +29,8 @@ mod imp {
pub invite: TemplateChild<Invite>,
#[template_child]
pub explore: TemplateChild<Explore>,
+ #[template_child]
+ pub empty_page: TemplateChild<gtk::Box>,
}
#[glib::object_subclass]
@@ -215,7 +217,7 @@ impl Content {
match self.content_type() {
ContentType::None => {
- //TODO: display an empty state
+ priv_.stack.set_visible_child(&*priv_.empty_page);
}
ContentType::Room => {
if let Some(room) = &*priv_.room.borrow() {
diff --git a/src/session/content/room_history.rs b/src/session/content/room_history.rs
index cb06ea94..498c4118 100644
--- a/src/session/content/room_history.rs
+++ b/src/session/content/room_history.rs
@@ -19,6 +19,7 @@ mod imp {
pub compact: Cell<bool>,
pub room: RefCell<Option<Room>>,
pub category_handler: RefCell<Option<SignalHandlerId>>,
+ pub empty_timeline_handler: RefCell<Option<SignalHandlerId>>,
pub md_enabled: Cell<bool>,
#[template_child]
pub headerbar: TemplateChild<adw::HeaderBar>,
@@ -34,6 +35,10 @@ mod imp {
pub message_entry: TemplateChild<sourceview::View>,
#[template_child]
pub markdown_button: TemplateChild<gtk::MenuButton>,
+ #[template_child]
+ pub loading: TemplateChild<gtk::Spinner>,
+ #[template_child]
+ pub stack: TemplateChild<gtk::Stack>,
}
#[glib::object_subclass]
@@ -83,6 +88,13 @@ mod imp {
Room::static_type(),
glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
),
+ glib::ParamSpec::new_boolean(
+ "empty",
+ "Empty",
+ "Wheter there is currently a room shown",
+ false,
+ glib::ParamFlags::READABLE,
+ ),
glib::ParamSpec::new_boolean(
"markdown-enabled",
"Markdown enabled",
@@ -129,23 +141,17 @@ mod imp {
match pspec.name() {
"compact" => self.compact.get().to_value(),
"room" => obj.room().to_value(),
+ "empty" => obj.room().is_none().to_value(),
"markdown-enabled" => self.md_enabled.get().to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self, obj: &Self::Type) {
- let adj = self.scrolled_window.vadjustment().unwrap();
- // TODO: make sure that we have enough messages to fill at least to scroll pages, if the room
history is long enough
+ let adj = self.listview.vadjustment().unwrap();
adj.connect_value_changed(clone!(@weak obj => move |adj| {
- // Load more message when the user gets close to the end of the known room history
- // Use the page size twice to detect if the user gets close the end
- if adj.value() < adj.page_size() * 2.0 {
- if let Some(room) = obj.room() {
- room.load_previous_events();
- }
- }
+ obj.load_more_messages(adj);
}));
let key_events = gtk::EventControllerKey::new();
@@ -218,6 +224,12 @@ impl RoomHistory {
}
}
+ if let Some(empty_timeline_handler) = priv_.empty_timeline_handler.take() {
+ if let Some(room) = self.room() {
+ room.timeline().disconnect(empty_timeline_handler);
+ }
+ }
+
if let Some(ref room) = room {
let handler_id = room.connect_notify_local(
Some("category"),
@@ -227,6 +239,15 @@ impl RoomHistory {
);
priv_.category_handler.replace(Some(handler_id));
+
+ let handler_id = room.timeline().connect_notify_local(
+ Some("empty"),
+ clone!(@weak self as obj => move |_, _| {
+ obj.set_empty_timeline();
+ }),
+ );
+
+ priv_.empty_timeline_handler.replace(Some(handler_id));
}
// TODO: use gtk::MultiSelection to allow selection
@@ -236,8 +257,12 @@ impl RoomHistory {
priv_.listview.set_model(model.as_ref());
priv_.room.replace(room);
+ let adj = priv_.listview.vadjustment().unwrap();
+ self.load_more_messages(&adj);
self.update_room_state();
+ self.set_empty_timeline();
self.notify("room");
+ self.notify("empty");
}
pub fn room(&self) -> Option<Room> {
@@ -279,4 +304,26 @@ impl RoomHistory {
}
}
}
+
+ fn set_empty_timeline(&self) {
+ let priv_ = imp::RoomHistory::from_instance(self);
+
+ if let Some(room) = &*priv_.room.borrow() {
+ if room.timeline().empty() {
+ priv_.stack.set_visible_child(&*priv_.loading);
+ } else {
+ priv_.stack.set_visible_child(&*priv_.scrolled_window);
+ }
+ }
+ }
+
+ fn load_more_messages(&self, adj: >k::Adjustment) {
+ // Load more message when the user gets close to the end of the known room history
+ // Use the page size twice to detect if the user gets close the end
+ if adj.value() < adj.page_size() * 2.0 || adj.upper() <= adj.page_size() * 2.0 {
+ if let Some(room) = self.room() {
+ room.load_previous_events();
+ }
+ }
+ }
}
diff --git a/src/session/room/timeline.rs b/src/session/room/timeline.rs
index 2ceb569e..4acc7c4e 100644
--- a/src/session/room/timeline.rs
+++ b/src/session/room/timeline.rs
@@ -39,13 +39,22 @@ mod imp {
impl ObjectImpl for Timeline {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
- vec![glib::ParamSpec::new_object(
- "room",
- "Room",
- "The Room containing this timeline",
- Room::static_type(),
- glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
- )]
+ vec![
+ glib::ParamSpec::new_object(
+ "room",
+ "Room",
+ "The Room containing this timeline",
+ Room::static_type(),
+ glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+ ),
+ glib::ParamSpec::new_boolean(
+ "empty",
+ "Empty",
+ "Whether the timeline is empty or not",
+ false,
+ glib::ParamFlags::READABLE,
+ ),
+ ]
});
PROPERTIES.as_ref()
@@ -67,9 +76,10 @@ mod imp {
}
}
- fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"room" => self.room.get().unwrap().to_value(),
+ "empty" => obj.empty().to_value(),
_ => unimplemented!(),
}
}
@@ -220,6 +230,8 @@ impl Timeline {
}
}
+ self.notify("empty");
+
self.upcast_ref::<gio::ListModel>()
.items_changed(position, removed, added);
}
@@ -390,4 +402,9 @@ impl Timeline {
let priv_ = imp::Timeline::from_instance(self);
priv_.room.get().unwrap()
}
+
+ pub fn empty(&self) -> bool {
+ let priv_ = imp::Timeline::from_instance(self);
+ priv_.list.borrow().is_empty()
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]