Drop Granite completely
This commit is contained in:
parent
e0a7a4326f
commit
748808d845
|
@ -20,7 +20,6 @@ If the options above are not available to you, you can build the app from source
|
||||||
- [x] valac
|
- [x] valac
|
||||||
- [x] libgtk-3-dev
|
- [x] libgtk-3-dev
|
||||||
- [x] libsoup2.4-dev
|
- [x] libsoup2.4-dev
|
||||||
- [x] libgranite-dev
|
|
||||||
- [x] libjson-glib-dev
|
- [x] libjson-glib-dev
|
||||||
- [ ] libhandy-1.0-dev (>= 0.83.0)
|
- [ ] libhandy-1.0-dev (>= 0.83.0)
|
||||||
|
|
||||||
|
|
10
data/app.css
10
data/app.css
|
@ -2,6 +2,12 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
.attachment {
|
.attachment {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: rgba (150, 150, 150, 0.2);
|
background: rgba (150, 150, 150, 0.2);
|
||||||
|
@ -11,10 +17,6 @@
|
||||||
.attachment .pic {
|
.attachment .pic {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
.attachment .chip {
|
|
||||||
padding: 6px;
|
|
||||||
border-radius:6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-title-button {
|
.header-title-button {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/accounts_button.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/accounts_button.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/accounts_button_item.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/accounts_button_item.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/profile_field_row.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/profile_field_row.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/timeline_filter.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/timeline_menu.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/list_item.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/list_item.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/list_editor_item.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/list_editor_item.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/widgets/attachment_slot.ui</file>
|
<file preprocess="xml-stripblanks">ui/widgets/attachment_slot.ui</file>
|
||||||
|
@ -18,5 +18,6 @@
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/main.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/main.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/preferences.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/preferences.ui</file>
|
||||||
<file preprocess="xml-stripblanks">ui/dialogs/list_editor.ui</file>
|
<file preprocess="xml-stripblanks">ui/dialogs/list_editor.ui</file>
|
||||||
|
<file preprocess="xml-stripblanks">ui/menus.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
<template class="TootleDialogsCompose" parent="GtkWindow">
|
<template class="TootleDialogsCompose" parent="GtkWindow">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="modal">True</property>
|
|
||||||
<property name="default_width">500</property>
|
<property name="default_width">500</property>
|
||||||
<property name="default_height">250</property>
|
<property name="default_height">250</property>
|
||||||
<property name="type_hint">dialog</property>
|
<property name="type_hint">dialog</property>
|
||||||
|
@ -360,6 +359,7 @@
|
||||||
<object class="GtkLabel" id="commit_label">
|
<object class="GtkLabel" id="commit_label">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Publish</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">ready</property>
|
<property name="name">ready</property>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">0</property>
|
<property name="name">0</property>
|
||||||
<property name="title" translatable="no">0</property>
|
<property name="title">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@ -145,6 +145,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Compose</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<property name="title" translatable="yes">Behavior</property>
|
<property name="title" translatable="yes">Behavior</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyActionRow">
|
<object class="HdyActionRow">
|
||||||
<property name="visible">True</property>
|
<property name="visible">False</property>
|
||||||
<property name="activatable_widget">autostart</property>
|
<property name="activatable_widget">autostart</property>
|
||||||
<property name="title" translatable="yes">Autostart</property>
|
<property name="title" translatable="yes">Autostart</property>
|
||||||
<property name="subtitle" translatable="yes">Start minimized at boot</property>
|
<property name="subtitle" translatable="yes">Start minimized at boot</property>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="HdyActionRow">
|
<object class="HdyActionRow">
|
||||||
<property name="visible">True</property>
|
<property name="visible">False</property>
|
||||||
<property name="activatable_widget">work_in_background</property>
|
<property name="activatable_widget">work_in_background</property>
|
||||||
<property name="title" translatable="yes">Background work</property>
|
<property name="title" translatable="yes">Background work</property>
|
||||||
<property name="subtitle" translatable="yes">Receive notifications even when the app window is closed</property>
|
<property name="subtitle" translatable="yes">Receive notifications even when the app window is closed</property>
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
|
||||||
|
<menu id="profile-menu">
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<submenu id="profile-filter-menu">
|
||||||
|
<attribute name="label" translatable="yes">Filter</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Posts</attribute>
|
||||||
|
<attribute name="action">view.source</attribute>
|
||||||
|
<attribute name="target">statuses</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Following</attribute>
|
||||||
|
<attribute name="action">view.source</attribute>
|
||||||
|
<attribute name="target">following</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Followers</attribute>
|
||||||
|
<attribute name="action">view.source</attribute>
|
||||||
|
<attribute name="target">followers</attribute>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">With Replies</attribute>
|
||||||
|
<attribute name="action">view.include-replies</attribute>
|
||||||
|
<attribute name="hidden-when">action-disabled</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">With Media</attribute>
|
||||||
|
<attribute name="action">view.only-media</attribute>
|
||||||
|
<attribute name="hidden-when">action-disabled</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</submenu>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Mention…</attribute>
|
||||||
|
<attribute name="action">view.mention</attribute>
|
||||||
|
<attribute name="target">public</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Direct Message…</attribute>
|
||||||
|
<attribute name="action">view.mention</attribute>
|
||||||
|
<attribute name="target">direct</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<!-- <submenu> -->
|
||||||
|
<!-- <attribute name="label" translatable="yes">Moderation</attribute> -->
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Hide Boosts</attribute>
|
||||||
|
<attribute name="action">view.hiding_reblogs</attribute>
|
||||||
|
<attribute name="hidden-when">action-disabled</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Mute</attribute>
|
||||||
|
<attribute name="action">view.muting</attribute>
|
||||||
|
</item>
|
||||||
|
<section>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Report</attribute>
|
||||||
|
<attribute name="action">view.report</attribute>
|
||||||
|
<!-- TODO: Reporting users -->
|
||||||
|
<attribute name="hidden-when">action-missing</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Block</attribute>
|
||||||
|
<attribute name="action">view.blocking</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">Block Domain</attribute>
|
||||||
|
<attribute name="action">view.domain_blocking</attribute>
|
||||||
|
<attribute name="hidden-when">action-disabled</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
<!-- </submenu> -->
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
|
||||||
|
</interface>
|
|
@ -54,10 +54,12 @@
|
||||||
<property name="height_request">128</property>
|
<property name="height_request">128</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="opacity">0.30196078431372547</property>
|
|
||||||
<property name="pixel_size">128</property>
|
<property name="pixel_size">128</property>
|
||||||
<property name="icon_name">com.github.bleakgrey.tootle-symbolic</property>
|
<property name="icon_name">com.github.bleakgrey.tootle-symbolic</property>
|
||||||
<property name="icon_size">0</property>
|
<property name="icon_size">0</property>
|
||||||
|
<style>
|
||||||
|
<class name="dim-label"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
@ -69,7 +71,6 @@
|
||||||
<object class="GtkStack" id="status_stack">
|
<object class="GtkStack" id="status_stack">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="opacity">0.5019607843137255</property>
|
|
||||||
<property name="transition_type">crossfade</property>
|
<property name="transition_type">crossfade</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="status_message_label">
|
<object class="GtkLabel" id="status_message_label">
|
||||||
|
@ -79,7 +80,6 @@
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">message</property>
|
<property name="name">message</property>
|
||||||
<property name="title" translatable="no">page0</property>
|
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -92,10 +92,12 @@
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="name">spinner</property>
|
<property name="name">spinner</property>
|
||||||
<property name="title" translatable="no">page1</property>
|
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="dim-label"/>
|
||||||
|
</style>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
|
|
@ -1,320 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Generated with glade 3.36.0 -->
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk+" version="3.22"/>
|
|
||||||
<object class="GtkPopover" id="popover">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_start">8</property>
|
|
||||||
<property name="margin_end">8</property>
|
|
||||||
<property name="margin_top">8</property>
|
|
||||||
<property name="margin_bottom">8</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">8</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Show:</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">8</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_source">
|
|
||||||
<property name="name">statuses</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">0 Posts</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_source_following">
|
|
||||||
<property name="name">following</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">radio_source</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">0 Follows</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_source_followers">
|
|
||||||
<property name="name">followers</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">radio_source</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">0 Followers</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRevealer" id="post_filter">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSeparator">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_top">8</property>
|
|
||||||
<property name="margin_bottom">8</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Filter:</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_top">8</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">8</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_post_filter">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="active">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">None</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_post_filter_with_replies">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">radio_post_filter</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Include Replies</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkRadioButton" id="radio_post_only_media">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="group">radio_post_filter</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Only Media</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<template class="TootleWidgetsTimelineFilter" parent="GtkMenuButton">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="focus_on_click">False</property>
|
|
||||||
<property name="receives_default">True</property>
|
|
||||||
<property name="draw_indicator">True</property>
|
|
||||||
<property name="popover">popover</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="title">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label">User</property>
|
|
||||||
<property name="ellipsize">end</property>
|
|
||||||
<property name="single_line_mode">True</property>
|
|
||||||
<attributes>
|
|
||||||
<attribute name="weight" value="bold"/>
|
|
||||||
</attributes>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkImage">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="icon_name">pan-down-symbolic</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="pack_type">end</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<style>
|
|
||||||
<class name="flat"/>
|
|
||||||
<class name="header-title-buttonx"/>
|
|
||||||
</style>
|
|
||||||
</template>
|
|
||||||
</interface>
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.36.0 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.22"/>
|
||||||
|
<template class="TootleWidgetsTimelineMenu" parent="GtkMenuButton">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="focus_on_click">False</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="title">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label">User</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
<property name="single_line_mode">True</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="bold"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">pan-down-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="flat"/>
|
||||||
|
<class name="header-title-buttonx"/>
|
||||||
|
</style>
|
||||||
|
</template>
|
||||||
|
<object class="GtkPopover" id="popover">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
26
meson.build
26
meson.build
|
@ -1,4 +1,15 @@
|
||||||
project('com.github.bleakgrey.tootle', 'vala', 'c')
|
project('com.github.bleakgrey.tootle', 'vala', 'c', version: '1.0.0')
|
||||||
|
|
||||||
|
config = configuration_data()
|
||||||
|
config.set('EXEC_NAME', meson.project_name())
|
||||||
|
config.set('GETTEXT_PACKAGE', meson.project_name())
|
||||||
|
config.set('RESOURCES', '/' + '/'.join(meson.project_name().split('.')) + '/' )
|
||||||
|
config.set('VERSION', meson.project_version())
|
||||||
|
config.set('PREFIX', get_option('prefix'))
|
||||||
|
config.set('NAME', 'Tootle')
|
||||||
|
config.set('WEBSITE', 'https://github.com/bleakgrey/tootle')
|
||||||
|
config.set('SUPPORT_WEBSITE', 'https://github.com/bleakgrey/tootle/issues')
|
||||||
|
config.set('COPYRIGHT', '© 2018-2020 bleak_grey')
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
i18n = import('i18n')
|
i18n = import('i18n')
|
||||||
|
@ -22,6 +33,12 @@ asresources = gnome.compile_resources(
|
||||||
c_name: 'as'
|
c_name: 'as'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
build_file = configure_file(
|
||||||
|
input: 'src/Build.vala',
|
||||||
|
output: 'Build.vala',
|
||||||
|
configuration: config
|
||||||
|
)
|
||||||
|
|
||||||
libhandy_dep = dependency('libhandy-1', version: '>= 0.83.0', required: false)
|
libhandy_dep = dependency('libhandy-1', version: '>= 0.83.0', required: false)
|
||||||
if not libhandy_dep.found()
|
if not libhandy_dep.found()
|
||||||
libhandy = subproject(
|
libhandy = subproject(
|
||||||
|
@ -44,12 +61,13 @@ endif
|
||||||
executable(
|
executable(
|
||||||
meson.project_name(),
|
meson.project_name(),
|
||||||
asresources,
|
asresources,
|
||||||
'src/Build.vala',
|
build_file,
|
||||||
'src/Application.vala',
|
'src/Application.vala',
|
||||||
'src/Desktop.vala',
|
'src/Desktop.vala',
|
||||||
'src/Drawing.vala',
|
'src/Drawing.vala',
|
||||||
'src/Html.vala',
|
'src/Html.vala',
|
||||||
'src/Request.vala',
|
'src/Request.vala',
|
||||||
|
'src/DateTime.vala',
|
||||||
'src/InstanceAccount.vala',
|
'src/InstanceAccount.vala',
|
||||||
'src/Services/Streams.vala',
|
'src/Services/Streams.vala',
|
||||||
'src/Services/Settings.vala',
|
'src/Services/Settings.vala',
|
||||||
|
@ -75,7 +93,7 @@ executable(
|
||||||
'src/Widgets/Widgetizable.vala',
|
'src/Widgets/Widgetizable.vala',
|
||||||
'src/Widgets/Avatar.vala',
|
'src/Widgets/Avatar.vala',
|
||||||
'src/Widgets/AccountsButton.vala',
|
'src/Widgets/AccountsButton.vala',
|
||||||
'src/Widgets/TimelineFilter.vala',
|
'src/Widgets/TimelineMenu.vala',
|
||||||
'src/Widgets/RichLabel.vala',
|
'src/Widgets/RichLabel.vala',
|
||||||
'src/Widgets/Status.vala',
|
'src/Widgets/Status.vala',
|
||||||
'src/Widgets/Notification.vala',
|
'src/Widgets/Notification.vala',
|
||||||
|
@ -86,6 +104,7 @@ executable(
|
||||||
'src/Widgets/Attachment/Picture.vala',
|
'src/Widgets/Attachment/Picture.vala',
|
||||||
'src/Dialogs/ISavedWindow.vala',
|
'src/Dialogs/ISavedWindow.vala',
|
||||||
'src/Dialogs/MainWindow.vala',
|
'src/Dialogs/MainWindow.vala',
|
||||||
|
'src/Dialogs/About.vala',
|
||||||
'src/Dialogs/Compose.vala',
|
'src/Dialogs/Compose.vala',
|
||||||
'src/Dialogs/Preferences.vala',
|
'src/Dialogs/Preferences.vala',
|
||||||
'src/Dialogs/ListEditor.vala',
|
'src/Dialogs/ListEditor.vala',
|
||||||
|
@ -109,7 +128,6 @@ executable(
|
||||||
dependency('gtk+-3.0', version: '>=3.22.0'),
|
dependency('gtk+-3.0', version: '>=3.22.0'),
|
||||||
dependency('glib-2.0', version: '>=2.30.0'),
|
dependency('glib-2.0', version: '>=2.30.0'),
|
||||||
dependency('gee-0.8', version: '>=0.8.5'),
|
dependency('gee-0.8', version: '>=0.8.5'),
|
||||||
dependency('granite', version: '>=5.2.0'),
|
|
||||||
dependency('libsoup-2.4'),
|
dependency('libsoup-2.4'),
|
||||||
dependency('json-glib-1.0', version: '>=1.4.4'),
|
dependency('json-glib-1.0', version: '>=1.4.4'),
|
||||||
libhandy_dep,
|
libhandy_dep,
|
||||||
|
|
|
@ -28,7 +28,9 @@ src/Drawing.vala
|
||||||
src/Html.vala
|
src/Html.vala
|
||||||
src/InstanceAccount.vala
|
src/InstanceAccount.vala
|
||||||
src/Request.vala
|
src/Request.vala
|
||||||
|
src/Time.vala
|
||||||
|
|
||||||
|
src/Dialogs/About.vala
|
||||||
src/Dialogs/Compose.vala
|
src/Dialogs/Compose.vala
|
||||||
src/Dialogs/ListEditor.vala
|
src/Dialogs/ListEditor.vala
|
||||||
src/Dialogs/MainWindow.vala
|
src/Dialogs/MainWindow.vala
|
||||||
|
|
|
@ -20,7 +20,6 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
||||||
public int64 followers_count { get; set; }
|
public int64 followers_count { get; set; }
|
||||||
public int64 following_count { get; set; }
|
public int64 following_count { get; set; }
|
||||||
public int64 statuses_count { get; set; }
|
public int64 statuses_count { get; set; }
|
||||||
public Relationship? rs { get; set; default = null; }
|
|
||||||
public Gee.ArrayList<API.AccountField>? fields { get; set; default = null; }
|
public Gee.ArrayList<API.AccountField>? fields { get; set; default = null; }
|
||||||
|
|
||||||
public string handle {
|
public string handle {
|
||||||
|
@ -28,6 +27,12 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
||||||
return "@" + acct;
|
return "@" + acct;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public string domain {
|
||||||
|
owned get {
|
||||||
|
var uri = new Soup.URI (url);
|
||||||
|
return uri.get_host ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Account from (Json.Node node) throws Error {
|
public static Account from (Json.Node node) throws Error {
|
||||||
return Entity.from_json (typeof (API.Account), node) as API.Account;
|
return Entity.from_json (typeof (API.Account), node) as API.Account;
|
||||||
|
@ -38,7 +43,7 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool is_local (InstanceAccount account) {
|
public override bool is_local (InstanceAccount account) {
|
||||||
return account.short_instance in url;
|
return account.domain in url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Gtk.Widget to_widget () {
|
public override Gtk.Widget to_widget () {
|
||||||
|
@ -61,53 +66,4 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Request get_relationship () {
|
|
||||||
return new Request.GET ("/api/v1/accounts/relationships")
|
|
||||||
.with_account (accounts.active)
|
|
||||||
.with_param ("id", id.to_string ())
|
|
||||||
.then ((sess, msg) => {
|
|
||||||
Network.parse_array (msg, node => {
|
|
||||||
rs = API.Relationship.from (node);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.on_error (network.on_error)
|
|
||||||
.exec ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Request set_following (bool state = true) {
|
|
||||||
var action = state ? "follow" : "unfollow";
|
|
||||||
return new Request.POST (@"/api/v1/accounts/$id/$action")
|
|
||||||
.with_account (accounts.active)
|
|
||||||
.then ((sess, msg) => {
|
|
||||||
var node = network.parse_node (msg);
|
|
||||||
rs = API.Relationship.from (node);
|
|
||||||
})
|
|
||||||
.on_error (network.on_error)
|
|
||||||
.exec ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Request set_muted (bool state = true) {
|
|
||||||
var action = state ? "mute" : "unmute";
|
|
||||||
return new Request.POST (@"/api/v1/accounts/$id/$action")
|
|
||||||
.with_account (accounts.active)
|
|
||||||
.then ((sess, msg) => {
|
|
||||||
var node = network.parse_node (msg);
|
|
||||||
rs = API.Relationship.from (node);
|
|
||||||
})
|
|
||||||
.on_error (network.on_error)
|
|
||||||
.exec ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Request set_blocked (bool state = true) {
|
|
||||||
var action = state ? "block" : "unblock";
|
|
||||||
return new Request.POST (@"/api/v1/accounts/$id/$action")
|
|
||||||
.with_account (accounts.active)
|
|
||||||
.then ((sess, msg) => {
|
|
||||||
var node = network.parse_node (msg);
|
|
||||||
rs = API.Relationship.from (node);
|
|
||||||
})
|
|
||||||
.on_error (network.on_error)
|
|
||||||
.exec ();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ using Json;
|
||||||
|
|
||||||
public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
|
public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
|
||||||
|
|
||||||
public static string[] ignore_props = {"formal", "handle", "short-instance", "has-spoiler"};
|
public static string[] ignore_props = {"formal", "handle", "domain", "has-spoiler"};
|
||||||
|
|
||||||
public virtual bool is_local (InstanceAccount account) {
|
public virtual bool is_local (InstanceAccount account) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -5,6 +5,12 @@ public class Tootle.API.Mention : Entity, Widgetizable {
|
||||||
public string acct { get; construct set; }
|
public string acct { get; construct set; }
|
||||||
public string url { get; construct set; }
|
public string url { get; construct set; }
|
||||||
|
|
||||||
|
public string handle {
|
||||||
|
owned get {
|
||||||
|
return "@" + acct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Mention.from_account (API.Account account) {
|
public Mention.from_account (API.Account account) {
|
||||||
Object (
|
Object (
|
||||||
id: account.id,
|
id: account.id,
|
||||||
|
|
|
@ -1,16 +1,55 @@
|
||||||
public class Tootle.API.Relationship : Entity {
|
public class Tootle.API.Relationship : Entity {
|
||||||
|
|
||||||
public string id { get; set; }
|
public string id { get; set; default = ""; }
|
||||||
public bool following { get; set; default = false; }
|
public bool following { get; set; default = false; }
|
||||||
public bool followed_by { get; set; default = false; }
|
public bool followed_by { get; set; default = false; }
|
||||||
public bool muting { get; set; default = false; }
|
public bool showing_reblogs { get; set; default = true; }
|
||||||
public bool muting_notifications { get; set; default = false; }
|
public bool muting { get; set; default = false; }
|
||||||
public bool requested { get; set; default = false; }
|
public bool muting_notifications { get; set; default = false; }
|
||||||
public bool blocking { get; set; default = false; }
|
public bool requested { get; set; default = false; }
|
||||||
public bool domain_blocking { get; set; default = false; }
|
public bool blocking { get; set; default = false; }
|
||||||
|
public bool domain_blocking { get; set; default = false; }
|
||||||
|
|
||||||
public static Relationship from (Json.Node node) throws Error {
|
public static Relationship from (Json.Node node) throws Error {
|
||||||
return Entity.from_json (typeof (API.Relationship), node) as API.Relationship;
|
return Entity.from_json (typeof (API.Relationship), node) as API.Relationship;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Relationship.for_account (API.Account acc) {
|
||||||
|
Object (id: acc.id);
|
||||||
|
request ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request () {
|
||||||
|
new Request.GET ("/api/v1/accounts/relationships")
|
||||||
|
.with_account (accounts.active)
|
||||||
|
.with_param ("id", id)
|
||||||
|
.then ((sess, msg) => {
|
||||||
|
Network.parse_array (msg, node => {
|
||||||
|
invalidate (node);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.exec ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidate (Json.Node node) throws Error {
|
||||||
|
var rs = Relationship.from (node);
|
||||||
|
patch (rs);
|
||||||
|
notify_property ("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modify (string operation, string? param = null, string? val = null) {
|
||||||
|
var req = new Request.POST (@"/api/v1/accounts/$id/$operation")
|
||||||
|
.with_account (accounts.active)
|
||||||
|
.then ((sess, msg) => {
|
||||||
|
var node = network.parse_node (msg);
|
||||||
|
invalidate (node);
|
||||||
|
message (@"Performed \"$operation\" on Relationship $id");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (param != null)
|
||||||
|
req.with_param (param, val);
|
||||||
|
|
||||||
|
req.exec ();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class Tootle.API.Status : Entity, Widgetizable {
|
||||||
public string get_reply_mentions () {
|
public string get_reply_mentions () {
|
||||||
var result = "";
|
var result = "";
|
||||||
if (account.acct != accounts.active.acct)
|
if (account.acct != accounts.active.acct)
|
||||||
result = "@%s ".printf (account.acct);
|
result = @"$(account.handle) ";
|
||||||
|
|
||||||
if (mentions != null) {
|
if (mentions != null) {
|
||||||
foreach (var mention in mentions) {
|
foreach (var mention in mentions) {
|
||||||
|
@ -101,7 +101,7 @@ public class Tootle.API.Status : Entity, Widgetizable {
|
||||||
var already_mentioned = mention.acct in result;
|
var already_mentioned = mention.acct in result;
|
||||||
|
|
||||||
if (!equals_current && ! already_mentioned)
|
if (!equals_current && ! already_mentioned)
|
||||||
result += "@%s ".printf (mention.acct);
|
result += @"$(mention.handle) ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,163 +1,177 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Granite;
|
|
||||||
|
|
||||||
namespace Tootle {
|
namespace Tootle {
|
||||||
|
|
||||||
public errordomain Oopsie {
|
public errordomain Oopsie {
|
||||||
USER,
|
USER,
|
||||||
PARSING,
|
PARSING,
|
||||||
INSTANCE,
|
INSTANCE,
|
||||||
INTERNAL
|
INTERNAL
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Application app;
|
public static Application app;
|
||||||
public static Dialogs.MainWindow? window;
|
public static Dialogs.MainWindow? window;
|
||||||
public static Window window_dummy;
|
public static Window window_dummy;
|
||||||
|
|
||||||
public static Settings settings;
|
public static Settings settings;
|
||||||
public static Accounts accounts;
|
public static Accounts accounts;
|
||||||
public static Network network;
|
public static Network network;
|
||||||
public static Cache cache;
|
public static Cache cache;
|
||||||
public static Streams streams;
|
public static Streams streams;
|
||||||
|
|
||||||
public static bool start_hidden = false;
|
public static bool start_hidden = false;
|
||||||
|
|
||||||
public class Application : Gtk.Application {
|
public class Application : Gtk.Application {
|
||||||
|
|
||||||
// These are used for the GTK Inspector
|
// These are used for the GTK Inspector
|
||||||
public Settings app_settings { get {return Tootle.settings; } }
|
public Settings app_settings { get {return Tootle.settings; } }
|
||||||
public Accounts app_accounts { get {return Tootle.accounts; } }
|
public Accounts app_accounts { get {return Tootle.accounts; } }
|
||||||
public Network app_network { get {return Tootle.network; } }
|
public Network app_network { get {return Tootle.network; } }
|
||||||
public Cache app_cache { get {return Tootle.cache; } }
|
public Cache app_cache { get {return Tootle.cache; } }
|
||||||
public Streams app_streams { get {return Tootle.streams; } }
|
public Streams app_streams { get {return Tootle.streams; } }
|
||||||
|
|
||||||
public signal void refresh ();
|
public signal void refresh ();
|
||||||
public signal void toast (string title);
|
public signal void toast (string title);
|
||||||
public signal void error (string title, string text);
|
public signal void error (string title, string text);
|
||||||
|
|
||||||
public const GLib.OptionEntry[] app_options = {
|
public const GLib.OptionEntry[] app_options = {
|
||||||
{ "hidden", 0, 0, OptionArg.NONE, ref start_hidden, "Do not show main window on start", null },
|
{ "hidden", 0, 0, OptionArg.NONE, ref start_hidden, "Do not show main window on start", null },
|
||||||
{ null }
|
{ null }
|
||||||
};
|
};
|
||||||
|
|
||||||
public const GLib.ActionEntry[] app_entries = {
|
public const GLib.ActionEntry[] app_entries = {
|
||||||
{"compose", compose_activated },
|
{ "about", about_activated },
|
||||||
{"back", back_activated },
|
{ "compose", compose_activated },
|
||||||
{"refresh", refresh_activated },
|
{ "back", back_activated },
|
||||||
{"switch-timeline", switch_timeline_activated, "i" }
|
{ "refresh", refresh_activated },
|
||||||
};
|
{ "switch-timeline", switch_timeline_activated, "i" }
|
||||||
|
};
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
application_id = Build.DOMAIN;
|
application_id = Build.DOMAIN;
|
||||||
flags = ApplicationFlags.FLAGS_NONE;
|
flags = ApplicationFlags.FLAGS_NONE;
|
||||||
}
|
|
||||||
|
|
||||||
public string[] ACCEL_NEW_POST = {"<Ctrl>T"};
|
|
||||||
public string[] ACCEL_BACK = {"<Alt>BackSpace", "<Alt>Left"};
|
|
||||||
public string[] ACCEL_REFRESH = {"<Ctrl>R", "F5"};
|
|
||||||
public string[] ACCEL_TIMELINE_0 = {"<Alt>1"};
|
|
||||||
public string[] ACCEL_TIMELINE_1 = {"<Alt>2"};
|
|
||||||
public string[] ACCEL_TIMELINE_2 = {"<Alt>3"};
|
|
||||||
public string[] ACCEL_TIMELINE_3 = {"<Alt>4"};
|
|
||||||
|
|
||||||
public static int main (string[] args) {
|
|
||||||
Gtk.init (ref args);
|
|
||||||
try {
|
|
||||||
var opt_context = new OptionContext ("- Options");
|
|
||||||
opt_context.add_main_entries (app_options, null);
|
|
||||||
opt_context.parse (ref args);
|
|
||||||
}
|
|
||||||
catch (GLib.OptionError e) {
|
|
||||||
warning (e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
app = new Application ();
|
|
||||||
return app.run (args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void startup () {
|
|
||||||
base.startup ();
|
|
||||||
Build.print_info ();
|
|
||||||
Hdy.init ();
|
|
||||||
|
|
||||||
settings = new Settings ();
|
|
||||||
streams = new Streams ();
|
|
||||||
accounts = new Accounts ();
|
|
||||||
network = new Network ();
|
|
||||||
cache = new Cache ();
|
|
||||||
accounts.init ();
|
|
||||||
|
|
||||||
app.error.connect (app.on_error);
|
|
||||||
|
|
||||||
window_dummy = new Window ();
|
|
||||||
add_window (window_dummy);
|
|
||||||
|
|
||||||
set_accels_for_action ("app.compose", ACCEL_NEW_POST);
|
|
||||||
set_accels_for_action ("app.back", ACCEL_BACK);
|
|
||||||
set_accels_for_action ("app.refresh", ACCEL_REFRESH);
|
|
||||||
set_accels_for_action ("app.switch-timeline(0)", ACCEL_TIMELINE_0);
|
|
||||||
set_accels_for_action ("app.switch-timeline(1)", ACCEL_TIMELINE_1);
|
|
||||||
set_accels_for_action ("app.switch-timeline(2)", ACCEL_TIMELINE_2);
|
|
||||||
set_accels_for_action ("app.switch-timeline(3)", ACCEL_TIMELINE_3);
|
|
||||||
add_action_entries (app_entries, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void activate () {
|
|
||||||
if (window != null) {
|
|
||||||
window.present ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start_hidden) {
|
|
||||||
start_hidden = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info ("Creating new window");
|
|
||||||
window = new Dialogs.MainWindow (this);
|
|
||||||
window.present ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void on_error (string title, string msg){
|
|
||||||
var message_dialog = new Granite.MessageDialog.with_image_from_icon_name (title, msg, "dialog-warning");
|
|
||||||
message_dialog.transient_for = window;
|
|
||||||
message_dialog.run ();
|
|
||||||
message_dialog.destroy ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void compose_activated () {
|
|
||||||
new Dialogs.Compose ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void back_activated () {
|
|
||||||
window.back ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void refresh_activated () {
|
|
||||||
refresh ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void switch_timeline_activated (SimpleAction a, Variant? v) {
|
|
||||||
int32 num = v.get_int32 ();
|
|
||||||
window.switch_timeline (num);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool question (string text, string? secondary = null, Gtk.Window? win = window) {
|
|
||||||
var dlg = new Gtk.MessageDialog (
|
|
||||||
window,
|
|
||||||
Gtk.DialogFlags.MODAL,
|
|
||||||
Gtk.MessageType.QUESTION,
|
|
||||||
Gtk.ButtonsType.YES_NO,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
dlg.text = text;
|
|
||||||
dlg.secondary_text = secondary;
|
|
||||||
dlg.transient_for = win;
|
|
||||||
var i = dlg.run ();
|
|
||||||
dlg.destroy ();
|
|
||||||
return i == ResponseType.YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public string[] ACCEL_ABOUT = {"F1"};
|
||||||
|
public string[] ACCEL_NEW_POST = {"<Ctrl>T"};
|
||||||
|
public string[] ACCEL_BACK = {"<Alt>BackSpace", "<Alt>Left"};
|
||||||
|
public string[] ACCEL_REFRESH = {"<Ctrl>R", "F5"};
|
||||||
|
public string[] ACCEL_TIMELINE_0 = {"<Alt>1"};
|
||||||
|
public string[] ACCEL_TIMELINE_1 = {"<Alt>2"};
|
||||||
|
public string[] ACCEL_TIMELINE_2 = {"<Alt>3"};
|
||||||
|
public string[] ACCEL_TIMELINE_3 = {"<Alt>4"};
|
||||||
|
|
||||||
|
public static int main (string[] args) {
|
||||||
|
Gtk.init (ref args);
|
||||||
|
try {
|
||||||
|
var opt_context = new OptionContext ("- Options");
|
||||||
|
opt_context.add_main_entries (app_options, null);
|
||||||
|
opt_context.parse (ref args);
|
||||||
|
}
|
||||||
|
catch (GLib.OptionError e) {
|
||||||
|
warning (e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
app = new Application ();
|
||||||
|
return app.run (args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void startup () {
|
||||||
|
base.startup ();
|
||||||
|
Build.print_info ();
|
||||||
|
Hdy.init ();
|
||||||
|
|
||||||
|
settings = new Settings ();
|
||||||
|
streams = new Streams ();
|
||||||
|
accounts = new Accounts ();
|
||||||
|
network = new Network ();
|
||||||
|
cache = new Cache ();
|
||||||
|
accounts.init ();
|
||||||
|
|
||||||
|
app.error.connect (app.on_error);
|
||||||
|
|
||||||
|
window_dummy = new Window ();
|
||||||
|
add_window (window_dummy);
|
||||||
|
|
||||||
|
set_accels_for_action ("app.about", ACCEL_ABOUT);
|
||||||
|
set_accels_for_action ("app.compose", ACCEL_NEW_POST);
|
||||||
|
set_accels_for_action ("app.back", ACCEL_BACK);
|
||||||
|
set_accels_for_action ("app.refresh", ACCEL_REFRESH);
|
||||||
|
set_accels_for_action ("app.switch-timeline(0)", ACCEL_TIMELINE_0);
|
||||||
|
set_accels_for_action ("app.switch-timeline(1)", ACCEL_TIMELINE_1);
|
||||||
|
set_accels_for_action ("app.switch-timeline(2)", ACCEL_TIMELINE_2);
|
||||||
|
set_accels_for_action ("app.switch-timeline(3)", ACCEL_TIMELINE_3);
|
||||||
|
add_action_entries (app_entries, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void activate () {
|
||||||
|
if (window != null) {
|
||||||
|
window.present ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_hidden) {
|
||||||
|
start_hidden = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info ("Creating new window");
|
||||||
|
window = new Dialogs.MainWindow (this);
|
||||||
|
window.present ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void compose_activated () {
|
||||||
|
new Dialogs.Compose ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void back_activated () {
|
||||||
|
window.back ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh_activated () {
|
||||||
|
refresh ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_timeline_activated (SimpleAction a, Variant? v) {
|
||||||
|
int32 num = v.get_int32 ();
|
||||||
|
window.switch_timeline (num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void about_activated () {
|
||||||
|
new Dialogs.About ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void on_error (string title, string msg){
|
||||||
|
var dlg = new Gtk.MessageDialog (
|
||||||
|
window,
|
||||||
|
Gtk.DialogFlags.MODAL,
|
||||||
|
Gtk.MessageType.ERROR,
|
||||||
|
Gtk.ButtonsType.OK,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
dlg.text = title;
|
||||||
|
dlg.secondary_text = msg;
|
||||||
|
dlg.transient_for = window;
|
||||||
|
dlg.run ();
|
||||||
|
dlg.destroy ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool question (string text, string? secondary = null, Gtk.Window? win = window) {
|
||||||
|
var dlg = new Gtk.MessageDialog (
|
||||||
|
window,
|
||||||
|
Gtk.DialogFlags.MODAL,
|
||||||
|
Gtk.MessageType.QUESTION,
|
||||||
|
Gtk.ButtonsType.YES_NO,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
dlg.text = text;
|
||||||
|
dlg.secondary_text = secondary;
|
||||||
|
dlg.transient_for = win;
|
||||||
|
var i = dlg.run ();
|
||||||
|
dlg.destroy ();
|
||||||
|
return i == ResponseType.YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,41 @@
|
||||||
public class Build {
|
public class Build {
|
||||||
|
|
||||||
public const string NAME = "Tootle";
|
public const string NAME = "@NAME@";
|
||||||
public const string WEBSITE = "https://github.com/bleakgrey/tootle";
|
public const string VERSION = "@VERSION@";
|
||||||
public const string DOMAIN = "com.github.bleakgrey.tootle";
|
public const string DOMAIN = "@EXEC_NAME@";
|
||||||
public const string RESOURCES = "/com/github/bleakgrey/tootle/";
|
public const string RESOURCES = "@RESOURCES@";
|
||||||
public const string VERSION = "1.0.0";
|
public const string WEBSITE = "@WEBSITE@";
|
||||||
|
public const string SUPPORT_WEBSITE = "@SUPPORT_WEBSITE@";
|
||||||
|
public const string COPYRIGHT = "@COPYRIGHT@";
|
||||||
|
public const string PREFIX = "@PREFIX@";
|
||||||
|
|
||||||
|
// Please do not remove the credits below. You may add your own, but keep the existing ones intact.
|
||||||
|
|
||||||
|
// TRANSLATORS: Replace this with your name. It will be displayed in the About dialog.
|
||||||
|
public const string TRANSLATOR = _(" ");
|
||||||
|
|
||||||
|
public static string[] get_authors () {
|
||||||
|
return new string[] {
|
||||||
|
"bleak_grey"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] get_artists () {
|
||||||
|
return new string[] {
|
||||||
|
"Tobias Bernard"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static void print_info () {
|
public static void print_info () {
|
||||||
var os_name = get_os_info ("NAME");
|
var os_name = get_os_info ("NAME");
|
||||||
var os_ver = get_os_info ("VERSION");
|
var os_ver = get_os_info ("VERSION");
|
||||||
message (@"$NAME $VERSION");
|
message (@"$NAME $VERSION");
|
||||||
message (@"Running on: $os_name $os_ver");
|
message (@"Running on: $os_name $os_ver");
|
||||||
message (@"Build type: FROM_SOURCE");
|
message (@"Build prefix: \"$PREFIX\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
static string get_os_info (string key) {
|
static string get_os_info (string key) {
|
||||||
var result = GLib.Environment.get_os_info (key);
|
return GLib.Environment.get_os_info (key) ?? "Unknown";
|
||||||
return result == null ? "Unknown" : result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
using GLib;
|
||||||
|
|
||||||
|
public class Tootle.DateTime {
|
||||||
|
|
||||||
|
public static string humanize (string iso8601) {
|
||||||
|
var date = new GLib.DateTime.from_iso8601 (iso8601, null);
|
||||||
|
var now = new GLib.DateTime.now_local ();
|
||||||
|
var delta = now.difference (date);
|
||||||
|
|
||||||
|
if (delta <= TimeSpan.MINUTE)
|
||||||
|
return _("Just now");
|
||||||
|
else if (delta < TimeSpan.HOUR) {
|
||||||
|
var minutes = delta / TimeSpan.MINUTE;
|
||||||
|
return _(@"$(minutes)m");
|
||||||
|
}
|
||||||
|
else if (delta <= TimeSpan.DAY) {
|
||||||
|
var hours = delta / TimeSpan.HOUR;
|
||||||
|
return _(@"$(hours)h");
|
||||||
|
}
|
||||||
|
else if (is_same_day (now, date.add_days (1))) {
|
||||||
|
return _("Yesterday");
|
||||||
|
}
|
||||||
|
else if (date.get_year () == now.get_year ()) {
|
||||||
|
return date.format (_("%b %e"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return date.format (_("%b %e, %Y"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool is_same_day (GLib.DateTime d1, GLib.DateTime d2) {
|
||||||
|
return (d1.get_day_of_year () == d2.get_day_of_year ()) && (d1.get_year () == d2.get_year ());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -93,8 +93,4 @@ public class Tootle.Desktop {
|
||||||
return theme.load_icon (name, 32, Gtk.IconLookupFlags.GENERIC_FALLBACK);
|
return theme.load_icon (name, 32, Gtk.IconLookupFlags.GENERIC_FALLBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set_hotkey_tooltip (Gtk.Widget widget, string? description, string[] accelerators) {
|
|
||||||
widget.tooltip_markup = Granite.markup_accel_tooltip (accelerators, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using Gtk;
|
||||||
|
|
||||||
|
public class Tootle.Dialogs.About : AboutDialog {
|
||||||
|
|
||||||
|
public About () {
|
||||||
|
Object (
|
||||||
|
transient_for: window,
|
||||||
|
modal: true,
|
||||||
|
|
||||||
|
logo_icon_name: Build.DOMAIN,
|
||||||
|
program_name: Build.NAME,
|
||||||
|
version: Build.VERSION,
|
||||||
|
website: Build.SUPPORT_WEBSITE,
|
||||||
|
website_label: _("Report an issue"),
|
||||||
|
license_type: License.GPL_3_0_ONLY,
|
||||||
|
copyright: Build.COPYRIGHT
|
||||||
|
);
|
||||||
|
|
||||||
|
// For some obscure reason, const arrays produce duplicates in the credits.
|
||||||
|
// Static functions seem to avoid this peculiar behavior.
|
||||||
|
authors = Build.get_authors ();
|
||||||
|
artists = Build.get_artists ();
|
||||||
|
translator_credits = Build.TRANSLATOR != " " ? Build.TRANSLATOR : null;
|
||||||
|
|
||||||
|
present ();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,8 +20,6 @@ public class Tootle.Dialogs.Compose : Window {
|
||||||
Button commit;
|
Button commit;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
Stack commit_stack;
|
Stack commit_stack;
|
||||||
[GtkChild]
|
|
||||||
Label commit_label;
|
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
Revealer cw_revealer;
|
Revealer cw_revealer;
|
||||||
|
@ -93,7 +91,7 @@ public class Tootle.Dialogs.Compose : Window {
|
||||||
|
|
||||||
notify["working"].connect (on_state_change);
|
notify["working"].connect (on_state_change);
|
||||||
|
|
||||||
commit_label.label = label;
|
mode_switcher.title = label;
|
||||||
commit.get_style_context ().add_class (style_class);
|
commit.get_style_context ().add_class (style_class);
|
||||||
|
|
||||||
visibility_popover = new Widgets.VisibilityPopover.with_button (visibility_button);
|
visibility_popover = new Widgets.VisibilityPopover.with_button (visibility_button);
|
||||||
|
@ -117,15 +115,16 @@ public class Tootle.Dialogs.Compose : Window {
|
||||||
validate ();
|
validate ();
|
||||||
set_media_mode (status.has_media ());
|
set_media_mode (status.has_media ());
|
||||||
show ();
|
show ();
|
||||||
|
content.grab_focus ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Compose () {
|
public Compose (API.Status template = new API.Status.empty ()) {
|
||||||
Object (
|
Object (
|
||||||
status: new API.Status.empty (),
|
status: template,
|
||||||
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
||||||
label: _("Publish")
|
label: _("Compose")
|
||||||
);
|
);
|
||||||
message ("Editing empty status");
|
message ("Composing status template");
|
||||||
set_visibility (status.visibility);
|
set_visibility (status.visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +134,8 @@ public class Tootle.Dialogs.Compose : Window {
|
||||||
style_class: STYLE_CLASS_DESTRUCTIVE_ACTION,
|
style_class: STYLE_CLASS_DESTRUCTIVE_ACTION,
|
||||||
label: _("Redraft")
|
label: _("Redraft")
|
||||||
);
|
);
|
||||||
set_visibility (status.visibility);
|
|
||||||
message (@"Redrafting status $(status.id)");
|
message (@"Redrafting status $(status.id)");
|
||||||
|
set_visibility (status.visibility);
|
||||||
status.media_attachments.@foreach (a => {
|
status.media_attachments.@foreach (a => {
|
||||||
media_list.insert (new MediaItem (this, null, a), 0);
|
media_list.insert (new MediaItem (this, null, a), 0);
|
||||||
return true;
|
return true;
|
||||||
|
@ -147,14 +146,15 @@ public class Tootle.Dialogs.Compose : Window {
|
||||||
var template = new API.Status.empty ();
|
var template = new API.Status.empty ();
|
||||||
template.in_reply_to_id = to.id.to_string ();
|
template.in_reply_to_id = to.id.to_string ();
|
||||||
template.in_reply_to_account_id = to.account.id.to_string ();
|
template.in_reply_to_account_id = to.account.id.to_string ();
|
||||||
|
template.spoiler_text = to.spoiler_text;
|
||||||
template.content = to.formal.get_reply_mentions ();
|
template.content = to.formal.get_reply_mentions ();
|
||||||
Object (
|
Object (
|
||||||
status: template,
|
status: template,
|
||||||
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
||||||
label: _("Reply")
|
label: _("Reply")
|
||||||
);
|
);
|
||||||
set_visibility (to.visibility);
|
|
||||||
message (@"Replying to status $(status.in_reply_to_id)");
|
message (@"Replying to status $(status.in_reply_to_id)");
|
||||||
|
set_visibility (to.visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_visibility (API.Visibility v) {
|
void set_visibility (API.Visibility v) {
|
||||||
|
|
|
@ -34,10 +34,7 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
back_button.clicked.connect (() => back ());
|
back_button.clicked.connect (() => back ());
|
||||||
Desktop.set_hotkey_tooltip (back_button, _("Back"), app.ACCEL_BACK);
|
|
||||||
|
|
||||||
compose_button.clicked.connect (() => new Dialogs.Compose ());
|
compose_button.clicked.connect (() => new Dialogs.Compose ());
|
||||||
Desktop.set_hotkey_tooltip (compose_button, _("Compose"), app.ACCEL_NEW_POST);
|
|
||||||
|
|
||||||
timeline_switcher.stack = timeline_stack;
|
timeline_switcher.stack = timeline_stack;
|
||||||
timeline_switcher.valign = Align.FILL;
|
timeline_switcher.valign = Align.FILL;
|
||||||
|
|
|
@ -15,17 +15,10 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
|
||||||
protected string? stream;
|
protected string? stream;
|
||||||
|
|
||||||
public new string handle {
|
public new string handle {
|
||||||
owned get { return @"@$username@$short_instance"; }
|
owned get { return @"@$username@$domain"; }
|
||||||
}
|
|
||||||
public string short_instance {
|
|
||||||
owned get {
|
|
||||||
return instance
|
|
||||||
.replace ("https://", "")
|
|
||||||
.replace ("/","");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceAccount from (Json.Node node) throws Error {
|
public new static InstanceAccount from (Json.Node node) throws Error {
|
||||||
return Entity.from_json (typeof (InstanceAccount), node) as InstanceAccount;
|
return Entity.from_json (typeof (InstanceAccount), node) as InstanceAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +67,7 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
|
||||||
var notification = new GLib.Notification (title);
|
var notification = new GLib.Notification (title);
|
||||||
if (obj.status != null) {
|
if (obj.status != null) {
|
||||||
var body = "";
|
var body = "";
|
||||||
body += short_instance;
|
body += domain;
|
||||||
body += "\n";
|
body += "\n";
|
||||||
body += Html.remove_tags (obj.status.content);
|
body += Html.remove_tags (obj.status.content);
|
||||||
notification.set_body (body);
|
notification.set_body (body);
|
||||||
|
|
|
@ -2,37 +2,43 @@ using Gtk;
|
||||||
|
|
||||||
public class Tootle.Views.Profile : Views.Timeline {
|
public class Tootle.Views.Profile : Views.Timeline {
|
||||||
|
|
||||||
public API.Account profile { get; construct set; }
|
public API.Account profile { get; construct set; }
|
||||||
|
public API.Relationship rs { get; construct set; }
|
||||||
|
public bool include_replies { get; set; default = false; }
|
||||||
|
public bool only_media { get; set; default = false; }
|
||||||
|
public string source { get; set; default = "statuses"; }
|
||||||
|
|
||||||
|
SimpleActionGroup actions;
|
||||||
|
SimpleAction media_action;
|
||||||
|
SimpleAction replies_action;
|
||||||
|
SimpleAction muting_action;
|
||||||
|
SimpleAction hiding_reblogs_action;
|
||||||
|
SimpleAction blocking_action;
|
||||||
|
SimpleAction domain_blocking_action;
|
||||||
|
|
||||||
ListBox profile_list;
|
ListBox profile_list;
|
||||||
|
Label relationship;
|
||||||
Label relationship;
|
Widgets.TimelineMenu menu_button;
|
||||||
Widgets.TimelineFilter filter;
|
|
||||||
|
|
||||||
Button rs_button;
|
Button rs_button;
|
||||||
Label rs_button_label;
|
Label rs_button_label;
|
||||||
|
|
||||||
weak ListBoxRow note_row;
|
weak ListBoxRow note_row;
|
||||||
|
|
||||||
public bool exclude_replies { get; set; default = true; }
|
construct {
|
||||||
public bool only_media { get; set; default = false; }
|
build_actions ();
|
||||||
|
menu_button = new Widgets.TimelineMenu ("profile-menu");
|
||||||
|
|
||||||
construct {
|
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/views/profile_header.ui");
|
||||||
profile.notify["rs"].connect (on_rs_updated);
|
profile_list = builder.get_object ("profile_list") as ListBox;
|
||||||
|
|
||||||
filter = new Widgets.TimelineFilter.with_profile (this);
|
var hdr = builder.get_object ("grid") as Grid;
|
||||||
|
|
||||||
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/views/profile_header.ui");
|
|
||||||
profile_list = builder.get_object ("profile_list") as ListBox;
|
|
||||||
|
|
||||||
var hdr = builder.get_object ("grid") as Grid;
|
|
||||||
column_view.pack_start (hdr, false, false, 0);
|
column_view.pack_start (hdr, false, false, 0);
|
||||||
column_view.reorder_child (hdr, 0);
|
column_view.reorder_child (hdr, 0);
|
||||||
|
|
||||||
var avatar = builder.get_object ("avatar") as Widgets.Avatar;
|
var avatar = builder.get_object ("avatar") as Widgets.Avatar;
|
||||||
avatar.url = profile.avatar;
|
avatar.url = profile.avatar;
|
||||||
|
|
||||||
profile.bind_property ("display-name", filter.title, "label", BindingFlags.SYNC_CREATE);
|
profile.bind_property ("display-name", menu_button.title, "label", BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
var handle = builder.get_object ("handle") as Widgets.RichLabel;
|
var handle = builder.get_object ("handle") as Widgets.RichLabel;
|
||||||
profile.bind_property ("acct", handle, "text", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
|
profile.bind_property ("acct", handle, "text", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
|
||||||
|
@ -51,62 +57,149 @@ public class Tootle.Views.Profile : Views.Timeline {
|
||||||
});
|
});
|
||||||
|
|
||||||
relationship = builder.get_object ("relationship") as Label;
|
relationship = builder.get_object ("relationship") as Label;
|
||||||
|
|
||||||
// posts_label = builder.get_object ("posts_label") as Label;
|
|
||||||
// profile.bind_property ("statuses_count", posts_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
|
|
||||||
// var val = (int64) src;
|
|
||||||
// target.set_string (_("%s Posts").printf (@"<b>$val</b>"));
|
|
||||||
// return true;
|
|
||||||
// });
|
|
||||||
// following_label = builder.get_object ("following_label") as Label;
|
|
||||||
// profile.bind_property ("following_count", following_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
|
|
||||||
// var val = (int64) src;
|
|
||||||
// target.set_string (_("%s Follows").printf (@"<b>$val</b>"));
|
|
||||||
// return true;
|
|
||||||
// });
|
|
||||||
// followers_label = builder.get_object ("followers_label") as Label;
|
|
||||||
// profile.bind_property ("followers_count", followers_label, "label", BindingFlags.SYNC_CREATE, (b, src, ref target) => {
|
|
||||||
// var val = (int64) src;
|
|
||||||
// target.set_string (_("%s Followers").printf (@"<b>$val</b>"));
|
|
||||||
// return true;
|
|
||||||
// });
|
|
||||||
|
|
||||||
rs_button = builder.get_object ("rs_button") as Button;
|
rs_button = builder.get_object ("rs_button") as Button;
|
||||||
rs_button.clicked.connect (on_rs_button_clicked);
|
rs_button.clicked.connect (on_rs_button_clicked);
|
||||||
rs_button_label = builder.get_object ("rs_button_label") as Label;
|
rs_button_label = builder.get_object ("rs_button_label") as Label;
|
||||||
|
rs.notify["id"].connect (on_rs_updated);
|
||||||
|
|
||||||
rebuild_fields ();
|
rebuild_fields ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Profile (API.Account acc) {
|
public Profile (API.Account acc) {
|
||||||
Object (
|
Object (
|
||||||
profile: acc,
|
profile: acc,
|
||||||
label: acc.acct,
|
rs: new API.Relationship.for_account (acc),
|
||||||
url: @"/api/v1/accounts/$(acc.id)/statuses"
|
label: acc.acct,
|
||||||
);
|
url: @"/api/v1/accounts/$(acc.id)/statuses"
|
||||||
profile.get_relationship ();
|
);
|
||||||
}
|
}
|
||||||
~Profile () {
|
~Profile () {
|
||||||
filter.destroy ();
|
menu_button.destroy ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void build_actions () {
|
||||||
|
actions = new SimpleActionGroup ();
|
||||||
|
|
||||||
|
media_action = new SimpleAction.stateful ("only-media", null, false);
|
||||||
|
media_action.change_state.connect (v => {
|
||||||
|
media_action.set_state (only_media = v.get_boolean ());
|
||||||
|
invalidate_actions (true);
|
||||||
|
});
|
||||||
|
actions.add_action (media_action);
|
||||||
|
|
||||||
|
replies_action = new SimpleAction.stateful ("include-replies", null, false);
|
||||||
|
replies_action.change_state.connect (v => {
|
||||||
|
replies_action.set_state (include_replies = v.get_boolean ());
|
||||||
|
invalidate_actions (true);
|
||||||
|
});
|
||||||
|
actions.add_action (replies_action);
|
||||||
|
|
||||||
|
var source_action = new SimpleAction.stateful ("source", VariantType.STRING, source);
|
||||||
|
source_action.change_state.connect (v => {
|
||||||
|
source = v.get_string ();
|
||||||
|
source_action.set_state (source);
|
||||||
|
accepts = source == "statuses" ? typeof (API.Status) : typeof (API.Account);
|
||||||
|
|
||||||
|
url = @"/api/v1/accounts/$(profile.id)/$source";
|
||||||
|
invalidate_actions (true);
|
||||||
|
});
|
||||||
|
actions.add_action (source_action);
|
||||||
|
|
||||||
|
var mention_action = new SimpleAction ("mention", VariantType.STRING);
|
||||||
|
mention_action.activate.connect (v => {
|
||||||
|
var status = new API.Status.empty ();
|
||||||
|
status.visibility = API.Visibility.from_string (v.get_string ());
|
||||||
|
status.content = @"$(profile.handle) ";
|
||||||
|
new Dialogs.Compose (status);
|
||||||
|
});
|
||||||
|
actions.add_action (mention_action);
|
||||||
|
|
||||||
|
muting_action = new SimpleAction.stateful ("muting", null, false);
|
||||||
|
muting_action.change_state.connect (v => {
|
||||||
|
var state = v.get_boolean ();
|
||||||
|
rs.modify (state ? "mute" : "unmute");
|
||||||
|
});
|
||||||
|
actions.add_action (muting_action);
|
||||||
|
|
||||||
|
hiding_reblogs_action = new SimpleAction.stateful ("hiding_reblogs", null, false);
|
||||||
|
hiding_reblogs_action.change_state.connect (v => {
|
||||||
|
var state = !v.get_boolean ();
|
||||||
|
rs.modify ("follow", "reblogs", @"$state");
|
||||||
|
});
|
||||||
|
actions.add_action (hiding_reblogs_action);
|
||||||
|
|
||||||
|
blocking_action = new SimpleAction.stateful ("blocking", null, false);
|
||||||
|
blocking_action.change_state.connect (v => {
|
||||||
|
var block = v.get_boolean ();
|
||||||
|
var q = block ? _("Block \"%s\"?") : _("Unblock \"%s\"?");
|
||||||
|
var yes = app.question (q.printf (profile.handle));
|
||||||
|
|
||||||
|
if (yes)
|
||||||
|
rs.modify (block ? "block" : "unblock");
|
||||||
|
});
|
||||||
|
actions.add_action (blocking_action);
|
||||||
|
|
||||||
|
domain_blocking_action = new SimpleAction.stateful ("domain_blocking", null, false);
|
||||||
|
domain_blocking_action.change_state.connect (v => {
|
||||||
|
var block = v.get_boolean ();
|
||||||
|
var q = block ? _("Block Entire \"%s\"?") : _("Unblock Entire \"%s\"?");
|
||||||
|
var yes = app.question (
|
||||||
|
q.printf (profile.domain),
|
||||||
|
_("Blocking a domain will:\n\n• Remove its public posts and notifications from your timelines\n• Remove its followers from your account\n• Prevent you from following its users")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (yes) {
|
||||||
|
var req = new Request.POST ("/api/v1/domain_blocks")
|
||||||
|
.with_account (accounts.active)
|
||||||
|
.with_param ("domain", profile.domain)
|
||||||
|
.then (() => {
|
||||||
|
rs.request ();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!block) req.method = "DELETE";
|
||||||
|
req.exec ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
actions.add_action (domain_blocking_action);
|
||||||
|
|
||||||
|
invalidate_actions (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidate_actions (bool refresh) {
|
||||||
|
replies_action.set_enabled (accepts == typeof (API.Status));
|
||||||
|
media_action.set_enabled (accepts == typeof (API.Status));
|
||||||
|
muting_action.set_state (rs.muting);
|
||||||
|
hiding_reblogs_action.set_state (!rs.showing_reblogs);
|
||||||
|
hiding_reblogs_action.set_enabled (rs.following);
|
||||||
|
blocking_action.set_state (rs.blocking);
|
||||||
|
domain_blocking_action.set_state (rs.domain_blocking);
|
||||||
|
domain_blocking_action.set_enabled (accounts.active.domain != profile.domain);
|
||||||
|
|
||||||
|
if (refresh) {
|
||||||
|
page_next = null;
|
||||||
|
on_refresh ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void on_shown () {
|
public override void on_shown () {
|
||||||
window.header.custom_title = filter;
|
window.insert_action_group ("view", actions);
|
||||||
filter.valign = Align.FILL;
|
window.header.custom_title = menu_button;
|
||||||
|
menu_button.valign = Align.FILL;
|
||||||
window.set_header_controls (rs_button);
|
window.set_header_controls (rs_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void on_hidden () {
|
public override void on_hidden () {
|
||||||
|
window.insert_action_group ("view", null);
|
||||||
window.header.custom_title = null;
|
window.header.custom_title = null;
|
||||||
window.reset_header_controls ();
|
window.reset_header_controls ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_rs_button_clicked () {
|
void on_rs_button_clicked () {
|
||||||
rs_button.sensitive = false;
|
rs_button.sensitive = false;
|
||||||
profile.set_following (!profile.rs.following);
|
rs.modify (rs.following ? "unfollow" : "follow");
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_rs_updated () {
|
void on_rs_updated () {
|
||||||
var rs = profile.rs;
|
|
||||||
var label = "";
|
var label = "";
|
||||||
if (rs_button.sensitive = rs != null) {
|
if (rs_button.sensitive = rs != null) {
|
||||||
if (rs.requested)
|
if (rs.requested)
|
||||||
|
@ -116,42 +209,39 @@ public class Tootle.Views.Profile : Views.Timeline {
|
||||||
else if (rs.followed_by)
|
else if (rs.followed_by)
|
||||||
label = _("Follows you");
|
label = _("Follows you");
|
||||||
|
|
||||||
foreach (Widget w in new Widget[] { rs_button }) {
|
var ctx = rs_button.get_style_context ();
|
||||||
var ctx = w.get_style_context ();
|
ctx.remove_class (STYLE_CLASS_SUGGESTED_ACTION);
|
||||||
ctx.remove_class (STYLE_CLASS_SUGGESTED_ACTION);
|
ctx.remove_class (STYLE_CLASS_DESTRUCTIVE_ACTION);
|
||||||
ctx.remove_class (STYLE_CLASS_DESTRUCTIVE_ACTION);
|
ctx.add_class (rs.following ? STYLE_CLASS_DESTRUCTIVE_ACTION : STYLE_CLASS_SUGGESTED_ACTION);
|
||||||
ctx.add_class (rs.following ? STYLE_CLASS_DESTRUCTIVE_ACTION : STYLE_CLASS_SUGGESTED_ACTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
rs_button_label.label = rs.following ? _("Unfollow") : _("Follow");
|
rs_button_label.label = rs.following ? _("Unfollow") : _("Follow");
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship.label = label;
|
relationship.label = label;
|
||||||
relationship.visible = label != "";
|
relationship.visible = label != "";
|
||||||
|
|
||||||
|
invalidate_actions (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Request append_params (Request req) {
|
public override Request append_params (Request req) {
|
||||||
if (page_next == null) {
|
if (page_next == null && source == "statuses") {
|
||||||
if (exclude_replies)
|
req.with_param ("exclude_replies", @"$(!include_replies)");
|
||||||
req.with_param ("exclude_replies", "true");
|
req.with_param ("only_media", @"$(only_media)");
|
||||||
if (only_media)
|
|
||||||
req.with_param ("only_media", "true");
|
|
||||||
return base.append_params (req);
|
return base.append_params (req);
|
||||||
}
|
}
|
||||||
else
|
else return req;
|
||||||
return req;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void open_from_id (string id) {
|
public static void open_from_id (string id) {
|
||||||
var msg = new Soup.Message ("GET", @"$(accounts.active.instance)/api/v1/accounts/$id");
|
var msg = new Soup.Message ("GET", @"$(accounts.active.instance)/api/v1/accounts/$id");
|
||||||
network.queue (msg, (sess, mess) => {
|
network.queue (msg, (sess, mess) => {
|
||||||
var node = network.parse_node (mess);
|
var node = network.parse_node (mess);
|
||||||
var acc = API.Account.from (node);
|
var acc = API.Account.from (node);
|
||||||
window.open_view (new Views.Profile (acc));
|
window.open_view (new Views.Profile (acc));
|
||||||
}, (status, reason) => {
|
}, (status, reason) => {
|
||||||
network.on_error (status, reason);
|
network.on_error (status, reason);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/profile_field_row.ui")]
|
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/profile_field_row.ui")]
|
||||||
protected class Field : ListBoxRow {
|
protected class Field : ListBoxRow {
|
||||||
|
|
|
@ -111,10 +111,12 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
|
||||||
})
|
})
|
||||||
.on_error (on_error);
|
.on_error (on_error);
|
||||||
req.exec ();
|
req.exec ();
|
||||||
|
|
||||||
return GLib.Source.REMOVE;
|
return GLib.Source.REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void on_refresh () {
|
public virtual void on_refresh () {
|
||||||
|
scrolled.vadjustment.value = 0;
|
||||||
status_button.sensitive = false;
|
status_button.sensitive = false;
|
||||||
clear ();
|
clear ();
|
||||||
status_message = STATUS_LOADING;
|
status_message = STATUS_LOADING;
|
||||||
|
|
|
@ -86,7 +86,6 @@ public class Tootle.Widgets.AccountsButton : Gtk.MenuButton, IAccountListener {
|
||||||
item_refresh.clicked.connect (() => {
|
item_refresh.clicked.connect (() => {
|
||||||
app.refresh ();
|
app.refresh ();
|
||||||
});
|
});
|
||||||
Desktop.set_hotkey_tooltip (item_refresh, null, app.ACCEL_REFRESH);
|
|
||||||
|
|
||||||
item_favs.clicked.connect (() => {
|
item_favs.clicked.connect (() => {
|
||||||
window.open_view (new Views.Favorites ());
|
window.open_view (new Views.Favorites ());
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Granite;
|
|
||||||
|
|
||||||
public class Tootle.Widgets.Notification : Widgets.Status {
|
public class Tootle.Widgets.Notification : Widgets.Status {
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,8 @@ public class Tootle.Widgets.Status : ListBoxRow {
|
||||||
|
|
||||||
protected string date {
|
protected string date {
|
||||||
owned get {
|
owned get {
|
||||||
var date = new GLib.DateTime.from_iso8601 (status.formal.created_at, null);
|
var date = status.formal.created_at;
|
||||||
var humanized = Granite.DateTime.get_relative_datetime (date);
|
return @"<small>$(DateTime.humanize (date))</small>";
|
||||||
return @"<small>$humanized</small>";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
using Gtk;
|
|
||||||
|
|
||||||
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/timeline_filter.ui")]
|
|
||||||
public class Tootle.Widgets.TimelineFilter : MenuButton {
|
|
||||||
|
|
||||||
weak Views.Profile view;
|
|
||||||
|
|
||||||
[GtkChild]
|
|
||||||
public Label title;
|
|
||||||
|
|
||||||
[GtkChild]
|
|
||||||
public RadioButton radio_source;
|
|
||||||
|
|
||||||
[GtkChild]
|
|
||||||
public Revealer post_filter;
|
|
||||||
[GtkChild]
|
|
||||||
public RadioButton radio_post_filter;
|
|
||||||
[GtkChild]
|
|
||||||
public RadioButton radio_post_only_media;
|
|
||||||
|
|
||||||
public string source { get; set; }
|
|
||||||
|
|
||||||
construct {
|
|
||||||
radio_source.bind_property ("active", post_filter, "reveal-child", BindingFlags.SYNC_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimelineFilter.with_profile (Views.Profile profile) {
|
|
||||||
this.view = profile;
|
|
||||||
radio_source.get_group ().@foreach (w => {
|
|
||||||
w.toggled.connect (() => {
|
|
||||||
if (w.active) {
|
|
||||||
source = w.name;
|
|
||||||
on_changed (view);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
radio_post_filter.get_group ().@foreach (w => {
|
|
||||||
w.toggled.connect (() => {
|
|
||||||
if (w.active)
|
|
||||||
on_changed (view);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_changed (Views.Profile view) {
|
|
||||||
var entity = typeof (API.Status);
|
|
||||||
if (source != "statuses")
|
|
||||||
entity = typeof (API.Account);
|
|
||||||
|
|
||||||
view.exclude_replies = radio_post_filter.active;
|
|
||||||
view.only_media = radio_post_only_media.active;
|
|
||||||
|
|
||||||
view.page_next = view.page_prev = null;
|
|
||||||
view.url = @"/api/v1/accounts/$(view.profile.id)/$source";
|
|
||||||
view.accepts = entity;
|
|
||||||
view.on_refresh ();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Gtk;
|
||||||
|
|
||||||
|
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/timeline_menu.ui")]
|
||||||
|
public class Tootle.Widgets.TimelineMenu : MenuButton {
|
||||||
|
|
||||||
|
[GtkChild]
|
||||||
|
public Label title;
|
||||||
|
|
||||||
|
public TimelineMenu (string id) {
|
||||||
|
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/menus.ui");
|
||||||
|
menu_model = builder.get_object (id) as MenuModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue