mirror of
https://gitlab.gnome.org/World/tootle
synced 2025-02-16 19:40:41 +01:00
Display thread lines
This commit is contained in:
parent
e28619a364
commit
f1f8cd1fb0
14
data/app.css
14
data/app.css
@ -40,3 +40,17 @@
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.ttl-post {
|
||||
padding: 0px;
|
||||
}
|
||||
.ttl-thread-line {
|
||||
background: @theme_fg_color;
|
||||
opacity: .1;
|
||||
margin-top: -8px;
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
|
||||
.ttl-large-body {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
@ -54,8 +54,8 @@
|
||||
<object class="HdyClamp" id="clamp">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="maximum_size">700</property>
|
||||
<property name="tightening_threshold">700</property>
|
||||
<property name="maximum_size">650</property>
|
||||
<property name="tightening_threshold">650</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="column_view">
|
||||
<property name="visible">True</property>
|
||||
|
@ -417,9 +417,26 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkImage" id="thread_line">
|
||||
<property name="width_request">4</property>
|
||||
<property name="height_request">32</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="icon_size">0</property>
|
||||
<style>
|
||||
<class name="ttl-thread-line"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="height">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="ttl-post"/>
|
||||
</style>
|
||||
</template>
|
||||
</interface>
|
||||
|
@ -2,88 +2,105 @@ using Gtk;
|
||||
|
||||
public class Tootle.Views.Thread : Views.Base, IAccountListener {
|
||||
|
||||
public API.Status root_status { get; construct set; }
|
||||
protected InstanceAccount? account = null;
|
||||
protected Widgets.Status root_widget;
|
||||
public API.Status root_status { get; construct set; }
|
||||
protected InstanceAccount? account = null;
|
||||
protected Widgets.Status root_widget;
|
||||
|
||||
public Thread (API.Status status) {
|
||||
Object (
|
||||
root_status: status,
|
||||
status_message: STATUS_LOADING,
|
||||
label: _("Conversation")
|
||||
);
|
||||
account_listener_init ();
|
||||
}
|
||||
public Thread (API.Status status) {
|
||||
Object (
|
||||
root_status: status,
|
||||
status_message: STATUS_LOADING,
|
||||
label: _("Conversation")
|
||||
);
|
||||
account_listener_init ();
|
||||
}
|
||||
|
||||
public override void on_account_changed (InstanceAccount? acc) {
|
||||
account = acc;
|
||||
request ();
|
||||
}
|
||||
public override void on_account_changed (InstanceAccount? acc) {
|
||||
account = acc;
|
||||
request ();
|
||||
}
|
||||
|
||||
Widgets.Status prepend (Entity entity, bool to_end = false){
|
||||
var w = entity.to_widget () as Widgets.Status;
|
||||
w.reveal_spoiler = true;
|
||||
Widgets.Status append (Entity entity){
|
||||
var w = entity.to_widget () as Widgets.Status;
|
||||
w.reveal_spoiler = true;
|
||||
content_list.insert (w, -1);
|
||||
return w;
|
||||
}
|
||||
|
||||
if (to_end)
|
||||
content_list.insert (w, -1);
|
||||
else
|
||||
content_list.prepend (w);
|
||||
void connect_threads () {
|
||||
Widgets.Status? last_w = null;
|
||||
string? last_id = null;
|
||||
|
||||
check_resize ();
|
||||
return w;
|
||||
}
|
||||
Widget append (Entity entity) {
|
||||
return prepend (entity, true);
|
||||
}
|
||||
content.get_children ().foreach (i => {
|
||||
var w = i as Widgets.Status;
|
||||
var id = w.status.formal.in_reply_to_id;
|
||||
|
||||
public void request () {
|
||||
new Request.GET (@"/api/v1/statuses/$(root_status.id)/context")
|
||||
.with_account (account)
|
||||
.with_ctx (this)
|
||||
.then ((sess, msg) => {
|
||||
var root = network.parse (msg);
|
||||
if (id == last_id) {
|
||||
Widgets.Status.ThreadRole.connect_posts (last_w, w);
|
||||
}
|
||||
|
||||
var ancestors = root.get_array_member ("ancestors");
|
||||
ancestors.foreach_element ((array, i, node) => {
|
||||
var status = Entity.from_json (typeof (API.Status), node);
|
||||
append (status);
|
||||
});
|
||||
last_w = w;
|
||||
last_id = w.status.formal.id;
|
||||
});
|
||||
|
||||
root_widget = append (root_status) as Widgets.Status;
|
||||
root_widget.expand_root ();
|
||||
content.get_children ().foreach (i => {
|
||||
var w = i as Widgets.Status;
|
||||
w.install_thread_line ();
|
||||
});
|
||||
|
||||
var descendants = root.get_array_member ("descendants");
|
||||
descendants.foreach_element ((array, i, node) => {
|
||||
var status = Entity.from_json (typeof (API.Status), node);
|
||||
append (status);
|
||||
});
|
||||
root_widget.thread_line.hide ();
|
||||
}
|
||||
|
||||
on_content_changed ();
|
||||
public void request () {
|
||||
new Request.GET (@"/api/v1/statuses/$(root_status.id)/context")
|
||||
.with_account (account)
|
||||
.with_ctx (this)
|
||||
.then ((sess, msg) => {
|
||||
|
||||
int x,y;
|
||||
translate_coordinates (root_widget, 0, header.get_allocated_height (), out x, out y);
|
||||
scrolled.vadjustment.value = (double)(y*-1);
|
||||
})
|
||||
.exec ();
|
||||
}
|
||||
var root = network.parse (msg);
|
||||
|
||||
public static void open_from_link (string q) {
|
||||
new Request.GET ("/api/v1/search")
|
||||
.with_account ()
|
||||
.with_param ("q", q)
|
||||
.with_param ("resolve", "true")
|
||||
.then ((sess, msg) => {
|
||||
var root = network.parse (msg);
|
||||
var statuses = root.get_array_member ("statuses");
|
||||
var node = statuses.get_element (0);
|
||||
if (node != null){
|
||||
var status = API.Status.from (node);
|
||||
window.open_view (new Views.Thread (status));
|
||||
}
|
||||
else
|
||||
Desktop.open_uri (q);
|
||||
})
|
||||
.exec ();
|
||||
}
|
||||
var ancestors = root.get_array_member ("ancestors");
|
||||
ancestors.foreach_element ((array, i, node) => {
|
||||
var status = Entity.from_json (typeof (API.Status), node);
|
||||
append (status);
|
||||
});
|
||||
|
||||
root_widget = append (root_status) as Widgets.Status;
|
||||
root_widget.expand_root ();
|
||||
|
||||
var descendants = root.get_array_member ("descendants");
|
||||
descendants.foreach_element ((array, i, node) => {
|
||||
var status = Entity.from_json (typeof (API.Status), node);
|
||||
append (status);
|
||||
});
|
||||
|
||||
connect_threads ();
|
||||
on_content_changed ();
|
||||
|
||||
int x,y;
|
||||
translate_coordinates (root_widget, 0, header.get_allocated_height (), out x, out y);
|
||||
scrolled.vadjustment.value = (double)(y*-1);
|
||||
})
|
||||
.exec ();
|
||||
}
|
||||
|
||||
public static void open_from_link (string q) {
|
||||
new Request.GET ("/api/v1/search")
|
||||
.with_account ()
|
||||
.with_param ("q", q)
|
||||
.with_param ("resolve", "true")
|
||||
.then ((sess, msg) => {
|
||||
var root = network.parse (msg);
|
||||
var statuses = root.get_array_member ("statuses");
|
||||
var node = statuses.get_element (0);
|
||||
if (node != null){
|
||||
var status = API.Status.from (node);
|
||||
window.open_view (new Views.Thread (status));
|
||||
}
|
||||
else
|
||||
Desktop.open_uri (q);
|
||||
})
|
||||
.exec ();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,10 +7,38 @@ public class Tootle.Widgets.Status : ListBoxRow {
|
||||
public API.Status status { get; construct set; }
|
||||
public API.NotificationType? kind { get; construct set; }
|
||||
|
||||
public enum ThreadRole {
|
||||
NONE,
|
||||
START,
|
||||
MIDDLE,
|
||||
END;
|
||||
|
||||
public static void connect_posts (Widgets.Status? prev, Widgets.Status curr) {
|
||||
if (prev == null) {
|
||||
curr.thread_role = NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (prev.thread_role) {
|
||||
case NONE:
|
||||
prev.thread_role = START;
|
||||
curr.thread_role = END;
|
||||
break;
|
||||
case END:
|
||||
prev.thread_role = MIDDLE;
|
||||
curr.thread_role = END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ThreadRole thread_role { get; set; default = ThreadRole.NONE; }
|
||||
|
||||
[GtkChild] protected Grid grid;
|
||||
|
||||
[GtkChild] protected Image header_icon;
|
||||
[GtkChild] protected Widgets.RichLabel header_label;
|
||||
[GtkChild] public Image thread_line;
|
||||
|
||||
[GtkChild] public Widgets.Avatar avatar;
|
||||
[GtkChild] protected Widgets.RichLabel name_label;
|
||||
@ -234,6 +262,7 @@ public class Tootle.Widgets.Status : ListBoxRow {
|
||||
public void expand_root () {
|
||||
activatable = false;
|
||||
content.selectable = true;
|
||||
content.get_style_context ().add_class ("ttl-large-body");
|
||||
|
||||
var parent = content_column.get_parent () as Container;
|
||||
var left_attach = parent.find_child_property ("left-attach");
|
||||
@ -242,4 +271,28 @@ public class Tootle.Widgets.Status : ListBoxRow {
|
||||
parent.set_child_property (content_column, 3, 2, width);
|
||||
}
|
||||
|
||||
public void install_thread_line () {
|
||||
var l = thread_line;
|
||||
switch (thread_role) {
|
||||
case NONE:
|
||||
l.visible = false;
|
||||
break;
|
||||
case START:
|
||||
l.valign = Align.FILL;
|
||||
l.margin_top = 24;
|
||||
l.visible = true;
|
||||
break;
|
||||
case MIDDLE:
|
||||
l.valign = Align.FILL;
|
||||
l.margin_top = 0;
|
||||
l.visible = true;
|
||||
break;
|
||||
case END:
|
||||
l.valign = Align.START;
|
||||
l.margin_top = 0;
|
||||
l.visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user