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] libgtk-3-dev
|
||||
- [x] libsoup2.4-dev
|
||||
- [x] libgranite-dev
|
||||
- [x] libjson-glib-dev
|
||||
- [ ] libhandy-1.0-dev (>= 0.83.0)
|
||||
|
||||
|
|
10
data/app.css
10
data/app.css
|
@ -2,6 +2,12 @@
|
|||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.attachment {
|
||||
border-radius: 6px;
|
||||
background: rgba (150, 150, 150, 0.2);
|
||||
|
@ -11,10 +17,6 @@
|
|||
.attachment .pic {
|
||||
border-radius: 6px;
|
||||
}
|
||||
.attachment .chip {
|
||||
padding: 6px;
|
||||
border-radius:6px;
|
||||
}
|
||||
|
||||
.header-title-button {
|
||||
margin: 0px;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<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/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_editor_item.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/preferences.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/dialogs/list_editor.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/menus.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<requires lib="gtk+" version="3.20"/>
|
||||
<template class="TootleDialogsCompose" parent="GtkWindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="default_width">500</property>
|
||||
<property name="default_height">250</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
|
@ -360,6 +359,7 @@
|
|||
<object class="GtkLabel" id="commit_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Publish</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">ready</property>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="name">0</property>
|
||||
<property name="title" translatable="no">0</property>
|
||||
<property name="title">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -145,6 +145,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Compose</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<property name="title" translatable="yes">Behavior</property>
|
||||
<child>
|
||||
<object class="HdyActionRow">
|
||||
<property name="visible">True</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="activatable_widget">autostart</property>
|
||||
<property name="title" translatable="yes">Autostart</property>
|
||||
<property name="subtitle" translatable="yes">Start minimized at boot</property>
|
||||
|
@ -56,7 +56,7 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="HdyActionRow">
|
||||
<property name="visible">True</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="activatable_widget">work_in_background</property>
|
||||
<property name="title" translatable="yes">Background work</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="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0.30196078431372547</property>
|
||||
<property name="pixel_size">128</property>
|
||||
<property name="icon_name">com.github.bleakgrey.tootle-symbolic</property>
|
||||
<property name="icon_size">0</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -69,7 +71,6 @@
|
|||
<object class="GtkStack" id="status_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0.5019607843137255</property>
|
||||
<property name="transition_type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="status_message_label">
|
||||
|
@ -79,7 +80,6 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="name">message</property>
|
||||
<property name="title" translatable="no">page0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -92,10 +92,12 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="name">spinner</property>
|
||||
<property name="title" translatable="no">page1</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<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')
|
||||
i18n = import('i18n')
|
||||
|
@ -22,6 +33,12 @@ asresources = gnome.compile_resources(
|
|||
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)
|
||||
if not libhandy_dep.found()
|
||||
libhandy = subproject(
|
||||
|
@ -44,12 +61,13 @@ endif
|
|||
executable(
|
||||
meson.project_name(),
|
||||
asresources,
|
||||
'src/Build.vala',
|
||||
build_file,
|
||||
'src/Application.vala',
|
||||
'src/Desktop.vala',
|
||||
'src/Drawing.vala',
|
||||
'src/Html.vala',
|
||||
'src/Request.vala',
|
||||
'src/DateTime.vala',
|
||||
'src/InstanceAccount.vala',
|
||||
'src/Services/Streams.vala',
|
||||
'src/Services/Settings.vala',
|
||||
|
@ -75,7 +93,7 @@ executable(
|
|||
'src/Widgets/Widgetizable.vala',
|
||||
'src/Widgets/Avatar.vala',
|
||||
'src/Widgets/AccountsButton.vala',
|
||||
'src/Widgets/TimelineFilter.vala',
|
||||
'src/Widgets/TimelineMenu.vala',
|
||||
'src/Widgets/RichLabel.vala',
|
||||
'src/Widgets/Status.vala',
|
||||
'src/Widgets/Notification.vala',
|
||||
|
@ -86,6 +104,7 @@ executable(
|
|||
'src/Widgets/Attachment/Picture.vala',
|
||||
'src/Dialogs/ISavedWindow.vala',
|
||||
'src/Dialogs/MainWindow.vala',
|
||||
'src/Dialogs/About.vala',
|
||||
'src/Dialogs/Compose.vala',
|
||||
'src/Dialogs/Preferences.vala',
|
||||
'src/Dialogs/ListEditor.vala',
|
||||
|
@ -109,7 +128,6 @@ executable(
|
|||
dependency('gtk+-3.0', version: '>=3.22.0'),
|
||||
dependency('glib-2.0', version: '>=2.30.0'),
|
||||
dependency('gee-0.8', version: '>=0.8.5'),
|
||||
dependency('granite', version: '>=5.2.0'),
|
||||
dependency('libsoup-2.4'),
|
||||
dependency('json-glib-1.0', version: '>=1.4.4'),
|
||||
libhandy_dep,
|
||||
|
|
|
@ -28,7 +28,9 @@ src/Drawing.vala
|
|||
src/Html.vala
|
||||
src/InstanceAccount.vala
|
||||
src/Request.vala
|
||||
src/Time.vala
|
||||
|
||||
src/Dialogs/About.vala
|
||||
src/Dialogs/Compose.vala
|
||||
src/Dialogs/ListEditor.vala
|
||||
src/Dialogs/MainWindow.vala
|
||||
|
|
|
@ -20,7 +20,6 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
|||
public int64 followers_count { get; set; }
|
||||
public int64 following_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 string handle {
|
||||
|
@ -28,6 +27,12 @@ public class Tootle.API.Account : Entity, Widgetizable {
|
|||
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 {
|
||||
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) {
|
||||
return account.short_instance in url;
|
||||
return account.domain in url;
|
||||
}
|
||||
|
||||
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 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) {
|
||||
return true;
|
||||
|
|
|
@ -5,6 +5,12 @@ public class Tootle.API.Mention : Entity, Widgetizable {
|
|||
public string acct { get; construct set; }
|
||||
public string url { get; construct set; }
|
||||
|
||||
public string handle {
|
||||
owned get {
|
||||
return "@" + acct;
|
||||
}
|
||||
}
|
||||
|
||||
public Mention.from_account (API.Account account) {
|
||||
Object (
|
||||
id: account.id,
|
||||
|
|
|
@ -1,16 +1,55 @@
|
|||
public class Tootle.API.Relationship : Entity {
|
||||
|
||||
public string id { get; set; }
|
||||
public bool following { get; set; default = false; }
|
||||
public bool followed_by { get; set; default = false; }
|
||||
public bool muting { get; set; default = false; }
|
||||
public bool muting_notifications { get; set; default = false; }
|
||||
public bool requested { get; set; default = false; }
|
||||
public bool blocking { get; set; default = false; }
|
||||
public bool domain_blocking { get; set; default = false; }
|
||||
public string id { get; set; default = ""; }
|
||||
public bool following { get; set; default = false; }
|
||||
public bool followed_by { get; set; default = false; }
|
||||
public bool showing_reblogs { get; set; default = true; }
|
||||
public bool muting { get; set; default = false; }
|
||||
public bool muting_notifications { get; set; default = false; }
|
||||
public bool requested { 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 {
|
||||
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 () {
|
||||
var result = "";
|
||||
if (account.acct != accounts.active.acct)
|
||||
result = "@%s ".printf (account.acct);
|
||||
result = @"$(account.handle) ";
|
||||
|
||||
if (mentions != null) {
|
||||
foreach (var mention in mentions) {
|
||||
|
@ -101,7 +101,7 @@ public class Tootle.API.Status : Entity, Widgetizable {
|
|||
var already_mentioned = mention.acct in result;
|
||||
|
||||
if (!equals_current && ! already_mentioned)
|
||||
result += "@%s ".printf (mention.acct);
|
||||
result += @"$(mention.handle) ";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,163 +1,177 @@
|
|||
using Gtk;
|
||||
using Granite;
|
||||
|
||||
namespace Tootle {
|
||||
|
||||
public errordomain Oopsie {
|
||||
USER,
|
||||
PARSING,
|
||||
INSTANCE,
|
||||
INTERNAL
|
||||
}
|
||||
public errordomain Oopsie {
|
||||
USER,
|
||||
PARSING,
|
||||
INSTANCE,
|
||||
INTERNAL
|
||||
}
|
||||
|
||||
public static Application app;
|
||||
public static Dialogs.MainWindow? window;
|
||||
public static Window window_dummy;
|
||||
public static Application app;
|
||||
public static Dialogs.MainWindow? window;
|
||||
public static Window window_dummy;
|
||||
|
||||
public static Settings settings;
|
||||
public static Accounts accounts;
|
||||
public static Network network;
|
||||
public static Cache cache;
|
||||
public static Streams streams;
|
||||
public static Settings settings;
|
||||
public static Accounts accounts;
|
||||
public static Network network;
|
||||
public static Cache cache;
|
||||
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
|
||||
public Settings app_settings { get {return Tootle.settings; } }
|
||||
public Accounts app_accounts { get {return Tootle.accounts; } }
|
||||
public Network app_network { get {return Tootle.network; } }
|
||||
public Cache app_cache { get {return Tootle.cache; } }
|
||||
public Streams app_streams { get {return Tootle.streams; } }
|
||||
// These are used for the GTK Inspector
|
||||
public Settings app_settings { get {return Tootle.settings; } }
|
||||
public Accounts app_accounts { get {return Tootle.accounts; } }
|
||||
public Network app_network { get {return Tootle.network; } }
|
||||
public Cache app_cache { get {return Tootle.cache; } }
|
||||
public Streams app_streams { get {return Tootle.streams; } }
|
||||
|
||||
public signal void refresh ();
|
||||
public signal void toast (string title);
|
||||
public signal void error (string title, string text);
|
||||
public signal void refresh ();
|
||||
public signal void toast (string title);
|
||||
public signal void error (string title, string text);
|
||||
|
||||
public const GLib.OptionEntry[] app_options = {
|
||||
{ "hidden", 0, 0, OptionArg.NONE, ref start_hidden, "Do not show main window on start", null },
|
||||
{ null }
|
||||
};
|
||||
public const GLib.OptionEntry[] app_options = {
|
||||
{ "hidden", 0, 0, OptionArg.NONE, ref start_hidden, "Do not show main window on start", null },
|
||||
{ null }
|
||||
};
|
||||
|
||||
public const GLib.ActionEntry[] app_entries = {
|
||||
{"compose", compose_activated },
|
||||
{"back", back_activated },
|
||||
{"refresh", refresh_activated },
|
||||
{"switch-timeline", switch_timeline_activated, "i" }
|
||||
};
|
||||
public const GLib.ActionEntry[] app_entries = {
|
||||
{ "about", about_activated },
|
||||
{ "compose", compose_activated },
|
||||
{ "back", back_activated },
|
||||
{ "refresh", refresh_activated },
|
||||
{ "switch-timeline", switch_timeline_activated, "i" }
|
||||
};
|
||||
|
||||
construct {
|
||||
application_id = Build.DOMAIN;
|
||||
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;
|
||||
construct {
|
||||
application_id = Build.DOMAIN;
|
||||
flags = ApplicationFlags.FLAGS_NONE;
|
||||
}
|
||||
|
||||
}
|
||||
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 const string NAME = "Tootle";
|
||||
public const string WEBSITE = "https://github.com/bleakgrey/tootle";
|
||||
public const string DOMAIN = "com.github.bleakgrey.tootle";
|
||||
public const string RESOURCES = "/com/github/bleakgrey/tootle/";
|
||||
public const string VERSION = "1.0.0";
|
||||
public const string NAME = "@NAME@";
|
||||
public const string VERSION = "@VERSION@";
|
||||
public const string DOMAIN = "@EXEC_NAME@";
|
||||
public const string RESOURCES = "@RESOURCES@";
|
||||
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 () {
|
||||
var os_name = get_os_info ("NAME");
|
||||
var os_ver = get_os_info ("VERSION");
|
||||
message (@"$NAME $VERSION");
|
||||
message (@"Running on: $os_name $os_ver");
|
||||
message (@"Build type: FROM_SOURCE");
|
||||
message (@"Build prefix: \"$PREFIX\"");
|
||||
}
|
||||
|
||||
static string get_os_info (string key) {
|
||||
var result = GLib.Environment.get_os_info (key);
|
||||
return result == null ? "Unknown" : result;
|
||||
return GLib.Environment.get_os_info (key) ?? "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
[GtkChild]
|
||||
Stack commit_stack;
|
||||
[GtkChild]
|
||||
Label commit_label;
|
||||
|
||||
[GtkChild]
|
||||
Revealer cw_revealer;
|
||||
|
@ -93,7 +91,7 @@ public class Tootle.Dialogs.Compose : Window {
|
|||
|
||||
notify["working"].connect (on_state_change);
|
||||
|
||||
commit_label.label = label;
|
||||
mode_switcher.title = label;
|
||||
commit.get_style_context ().add_class (style_class);
|
||||
|
||||
visibility_popover = new Widgets.VisibilityPopover.with_button (visibility_button);
|
||||
|
@ -117,15 +115,16 @@ public class Tootle.Dialogs.Compose : Window {
|
|||
validate ();
|
||||
set_media_mode (status.has_media ());
|
||||
show ();
|
||||
content.grab_focus ();
|
||||
}
|
||||
|
||||
public Compose () {
|
||||
public Compose (API.Status template = new API.Status.empty ()) {
|
||||
Object (
|
||||
status: new API.Status.empty (),
|
||||
status: template,
|
||||
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
||||
label: _("Publish")
|
||||
label: _("Compose")
|
||||
);
|
||||
message ("Editing empty status");
|
||||
message ("Composing status template");
|
||||
set_visibility (status.visibility);
|
||||
}
|
||||
|
||||
|
@ -135,8 +134,8 @@ public class Tootle.Dialogs.Compose : Window {
|
|||
style_class: STYLE_CLASS_DESTRUCTIVE_ACTION,
|
||||
label: _("Redraft")
|
||||
);
|
||||
set_visibility (status.visibility);
|
||||
message (@"Redrafting status $(status.id)");
|
||||
set_visibility (status.visibility);
|
||||
status.media_attachments.@foreach (a => {
|
||||
media_list.insert (new MediaItem (this, null, a), 0);
|
||||
return true;
|
||||
|
@ -147,14 +146,15 @@ public class Tootle.Dialogs.Compose : Window {
|
|||
var template = new API.Status.empty ();
|
||||
template.in_reply_to_id = to.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 ();
|
||||
Object (
|
||||
status: template,
|
||||
style_class: STYLE_CLASS_SUGGESTED_ACTION,
|
||||
label: _("Reply")
|
||||
);
|
||||
set_visibility (to.visibility);
|
||||
message (@"Replying to status $(status.in_reply_to_id)");
|
||||
set_visibility (to.visibility);
|
||||
}
|
||||
|
||||
void set_visibility (API.Visibility v) {
|
||||
|
|
|
@ -34,10 +34,7 @@ public class Tootle.Dialogs.MainWindow: Gtk.Window, ISavedWindow {
|
|||
|
||||
construct {
|
||||
back_button.clicked.connect (() => back ());
|
||||
Desktop.set_hotkey_tooltip (back_button, _("Back"), app.ACCEL_BACK);
|
||||
|
||||
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.valign = Align.FILL;
|
||||
|
|
|
@ -15,17 +15,10 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
|
|||
protected string? stream;
|
||||
|
||||
public new string handle {
|
||||
owned get { return @"@$username@$short_instance"; }
|
||||
}
|
||||
public string short_instance {
|
||||
owned get {
|
||||
return instance
|
||||
.replace ("https://", "")
|
||||
.replace ("/","");
|
||||
}
|
||||
owned get { return @"@$username@$domain"; }
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -74,7 +67,7 @@ public class Tootle.InstanceAccount : API.Account, IStreamListener {
|
|||
var notification = new GLib.Notification (title);
|
||||
if (obj.status != null) {
|
||||
var body = "";
|
||||
body += short_instance;
|
||||
body += domain;
|
||||
body += "\n";
|
||||
body += Html.remove_tags (obj.status.content);
|
||||
notification.set_body (body);
|
||||
|
|
|
@ -2,37 +2,43 @@ using Gtk;
|
|||
|
||||
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;
|
||||
|
||||
Label relationship;
|
||||
Widgets.TimelineFilter filter;
|
||||
|
||||
Label relationship;
|
||||
Widgets.TimelineMenu menu_button;
|
||||
Button rs_button;
|
||||
Label rs_button_label;
|
||||
|
||||
weak ListBoxRow note_row;
|
||||
|
||||
public bool exclude_replies { get; set; default = true; }
|
||||
public bool only_media { get; set; default = false; }
|
||||
construct {
|
||||
build_actions ();
|
||||
menu_button = new Widgets.TimelineMenu ("profile-menu");
|
||||
|
||||
construct {
|
||||
profile.notify["rs"].connect (on_rs_updated);
|
||||
var builder = new Builder.from_resource (@"$(Build.RESOURCES)ui/views/profile_header.ui");
|
||||
profile_list = builder.get_object ("profile_list") as ListBox;
|
||||
|
||||
filter = new Widgets.TimelineFilter.with_profile (this);
|
||||
|
||||
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;
|
||||
var hdr = builder.get_object ("grid") as Grid;
|
||||
column_view.pack_start (hdr, false, false, 0);
|
||||
column_view.reorder_child (hdr, 0);
|
||||
|
||||
var avatar = builder.get_object ("avatar") as Widgets.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;
|
||||
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;
|
||||
|
||||
// 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.clicked.connect (on_rs_button_clicked);
|
||||
rs_button_label = builder.get_object ("rs_button_label") as Label;
|
||||
rs.notify["id"].connect (on_rs_updated);
|
||||
|
||||
rebuild_fields ();
|
||||
}
|
||||
}
|
||||
|
||||
public Profile (API.Account acc) {
|
||||
Object (
|
||||
profile: acc,
|
||||
label: acc.acct,
|
||||
url: @"/api/v1/accounts/$(acc.id)/statuses"
|
||||
);
|
||||
profile.get_relationship ();
|
||||
}
|
||||
~Profile () {
|
||||
filter.destroy ();
|
||||
}
|
||||
public Profile (API.Account acc) {
|
||||
Object (
|
||||
profile: acc,
|
||||
rs: new API.Relationship.for_account (acc),
|
||||
label: acc.acct,
|
||||
url: @"/api/v1/accounts/$(acc.id)/statuses"
|
||||
);
|
||||
}
|
||||
~Profile () {
|
||||
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 () {
|
||||
window.header.custom_title = filter;
|
||||
filter.valign = Align.FILL;
|
||||
window.insert_action_group ("view", actions);
|
||||
window.header.custom_title = menu_button;
|
||||
menu_button.valign = Align.FILL;
|
||||
window.set_header_controls (rs_button);
|
||||
}
|
||||
|
||||
public override void on_hidden () {
|
||||
window.insert_action_group ("view", null);
|
||||
window.header.custom_title = null;
|
||||
window.reset_header_controls ();
|
||||
}
|
||||
|
||||
void on_rs_button_clicked () {
|
||||
rs_button.sensitive = false;
|
||||
profile.set_following (!profile.rs.following);
|
||||
rs.modify (rs.following ? "unfollow" : "follow");
|
||||
}
|
||||
|
||||
void on_rs_updated () {
|
||||
var rs = profile.rs;
|
||||
var label = "";
|
||||
if (rs_button.sensitive = rs != null) {
|
||||
if (rs.requested)
|
||||
|
@ -116,42 +209,39 @@ public class Tootle.Views.Profile : Views.Timeline {
|
|||
else if (rs.followed_by)
|
||||
label = _("Follows you");
|
||||
|
||||
foreach (Widget w in new Widget[] { rs_button }) {
|
||||
var ctx = w.get_style_context ();
|
||||
ctx.remove_class (STYLE_CLASS_SUGGESTED_ACTION);
|
||||
ctx.remove_class (STYLE_CLASS_DESTRUCTIVE_ACTION);
|
||||
ctx.add_class (rs.following ? STYLE_CLASS_DESTRUCTIVE_ACTION : STYLE_CLASS_SUGGESTED_ACTION);
|
||||
}
|
||||
var ctx = rs_button.get_style_context ();
|
||||
ctx.remove_class (STYLE_CLASS_SUGGESTED_ACTION);
|
||||
ctx.remove_class (STYLE_CLASS_DESTRUCTIVE_ACTION);
|
||||
ctx.add_class (rs.following ? STYLE_CLASS_DESTRUCTIVE_ACTION : STYLE_CLASS_SUGGESTED_ACTION);
|
||||
|
||||
rs_button_label.label = rs.following ? _("Unfollow") : _("Follow");
|
||||
}
|
||||
|
||||
relationship.label = label;
|
||||
relationship.visible = label != "";
|
||||
|
||||
invalidate_actions (false);
|
||||
}
|
||||
|
||||
public override Request append_params (Request req) {
|
||||
if (page_next == null) {
|
||||
if (exclude_replies)
|
||||
req.with_param ("exclude_replies", "true");
|
||||
if (only_media)
|
||||
req.with_param ("only_media", "true");
|
||||
if (page_next == null && source == "statuses") {
|
||||
req.with_param ("exclude_replies", @"$(!include_replies)");
|
||||
req.with_param ("only_media", @"$(only_media)");
|
||||
return base.append_params (req);
|
||||
}
|
||||
else
|
||||
return req;
|
||||
else return req;
|
||||
}
|
||||
|
||||
public static void open_from_id (string id) {
|
||||
var msg = new Soup.Message ("GET", @"$(accounts.active.instance)/api/v1/accounts/$id");
|
||||
network.queue (msg, (sess, mess) => {
|
||||
var node = network.parse_node (mess);
|
||||
var acc = API.Account.from (node);
|
||||
window.open_view (new Views.Profile (acc));
|
||||
}, (status, reason) => {
|
||||
network.on_error (status, reason);
|
||||
});
|
||||
}
|
||||
public static void open_from_id (string id) {
|
||||
var msg = new Soup.Message ("GET", @"$(accounts.active.instance)/api/v1/accounts/$id");
|
||||
network.queue (msg, (sess, mess) => {
|
||||
var node = network.parse_node (mess);
|
||||
var acc = API.Account.from (node);
|
||||
window.open_view (new Views.Profile (acc));
|
||||
}, (status, reason) => {
|
||||
network.on_error (status, reason);
|
||||
});
|
||||
}
|
||||
|
||||
[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/profile_field_row.ui")]
|
||||
protected class Field : ListBoxRow {
|
||||
|
|
|
@ -111,10 +111,12 @@ public class Tootle.Views.Timeline : IAccountListener, IStreamListener, Views.Ba
|
|||
})
|
||||
.on_error (on_error);
|
||||
req.exec ();
|
||||
|
||||
return GLib.Source.REMOVE;
|
||||
}
|
||||
|
||||
public virtual void on_refresh () {
|
||||
scrolled.vadjustment.value = 0;
|
||||
status_button.sensitive = false;
|
||||
clear ();
|
||||
status_message = STATUS_LOADING;
|
||||
|
|
|
@ -86,7 +86,6 @@ public class Tootle.Widgets.AccountsButton : Gtk.MenuButton, IAccountListener {
|
|||
item_refresh.clicked.connect (() => {
|
||||
app.refresh ();
|
||||
});
|
||||
Desktop.set_hotkey_tooltip (item_refresh, null, app.ACCEL_REFRESH);
|
||||
|
||||
item_favs.clicked.connect (() => {
|
||||
window.open_view (new Views.Favorites ());
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using Gtk;
|
||||
using Granite;
|
||||
|
||||
public class Tootle.Widgets.Notification : Widgets.Status {
|
||||
|
||||
|
|
|
@ -74,9 +74,8 @@ public class Tootle.Widgets.Status : ListBoxRow {
|
|||
|
||||
protected string date {
|
||||
owned get {
|
||||
var date = new GLib.DateTime.from_iso8601 (status.formal.created_at, null);
|
||||
var humanized = Granite.DateTime.get_relative_datetime (date);
|
||||
return @"<small>$humanized</small>";
|
||||
var date = status.formal.created_at;
|
||||
return @"<small>$(DateTime.humanize (date))</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