Use ArrayList in Watchlist

This commit is contained in:
bleakgrey 2018-10-27 11:29:25 +03:00
parent 474f67b5a5
commit 11a01f7fea
9 changed files with 109 additions and 98 deletions

1
debian/control vendored
View File

@ -8,6 +8,7 @@ Build-Depends: meson,
libgranite-dev, libgranite-dev,
libgtk-3-dev (>= 3.22.0), libgtk-3-dev (>= 3.22.0),
libglib2.0-dev (>= 2.30.0), libglib2.0-dev (>= 2.30.0),
libgee-0.8-dev (>= 0.8.5),
libsoup2.4-dev, libsoup2.4-dev,
libjson-glib-dev libjson-glib-dev
Standards-Version: 3.9.3 Standards-Version: 3.9.3

View File

@ -68,6 +68,7 @@ executable(
dependencies: [ dependencies: [
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('granite'), dependency('granite'),
dependency('json-glib-1.0'), dependency('json-glib-1.0'),
dependency('libsoup-2.4') dependency('libsoup-2.4')

View File

@ -1,4 +1,6 @@
public class Tootle.Relationship{ using GLib;
public class Tootle.Relationship : Object {
public int64 id; public int64 id;
public bool following; public bool following;
@ -9,11 +11,11 @@ public class Tootle.Relationship{
public bool requested; public bool requested;
public bool domain_blocking; public bool domain_blocking;
public Relationship (int64 _id){ public Relationship (int64 _id) {
id = _id; id = _id;
} }
public static Relationship parse (Json.Object obj){ public static Relationship parse (Json.Object obj) {
var id = int64.parse (obj.get_string_member ("id")); var id = int64.parse (obj.get_string_member ("id"));
var relationship = new Relationship (id); var relationship = new Relationship (id);
relationship.following = obj.get_boolean_member ("following"); relationship.following = obj.get_boolean_member ("following");

View File

@ -1,30 +1,30 @@
using GLib; using GLib;
public class Tootle.Accounts : Object{ public class Tootle.Accounts : Object {
private string dir_path; private string dir_path;
private string file_path; private string file_path;
public abstract signal void switched (Account? account); public signal void switched (Account? account);
public abstract signal void updated (GenericArray<InstanceAccount> accounts); public signal void updated (GenericArray<InstanceAccount> accounts);
public GenericArray<InstanceAccount> saved_accounts = new GenericArray<InstanceAccount> (); public GenericArray<InstanceAccount> saved_accounts = new GenericArray<InstanceAccount> ();
public InstanceAccount? formal {get; set;} public InstanceAccount? formal {get; set;}
public Account? current {get; set;} public Account? current {get; set;}
public Accounts () { public Accounts () {
dir_path = "%s/%s".printf (GLib.Environment.get_user_config_dir (), Tootle.app.application_id); dir_path = "%s/%s".printf (GLib.Environment.get_user_config_dir (), app.application_id);
file_path = "%s/%s".printf (dir_path, "accounts.json"); file_path = "%s/%s".printf (dir_path, "accounts.json");
} }
public void switch_account (int id){ public void switch_account (int id) {
debug ("Switching to account #%i", id); debug ("Switching to #%i", id);
Tootle.settings.current_account = id; settings.current_account = id;
formal = saved_accounts.@get(id); formal = saved_accounts.@get (id);
var msg = new Soup.Message("GET", "%s/api/v1/accounts/verify_credentials".printf (Tootle.accounts.formal.instance)); var msg = new Soup.Message("GET", "%s/api/v1/accounts/verify_credentials".printf (accounts.formal.instance));
Tootle.network.queue(msg, (sess, mess) => { network.queue (msg, (sess, mess) => {
try{ try {
var root = Tootle.network.parse (mess); var root = network.parse (mess);
current = Account.parse (root); current = Account.parse (root);
switched (current); switched (current);
updated (saved_accounts); updated (saved_accounts);
@ -53,7 +53,7 @@ public class Tootle.Accounts : Object{
if (saved_accounts.length < 1) if (saved_accounts.length < 1)
switched (null); switched (null);
else { else {
var id = Tootle.settings.current_account - 1; var id = settings.current_account - 1;
if (id > saved_accounts.length - 1) if (id > saved_accounts.length - 1)
id = saved_accounts.length - 1; id = saved_accounts.length - 1;
else if (id < saved_accounts.length - 1) else if (id < saved_accounts.length - 1)
@ -64,7 +64,7 @@ public class Tootle.Accounts : Object{
updated (saved_accounts); updated (saved_accounts);
if (is_empty ()) { if (is_empty ()) {
Tootle.window.destroy (); window.destroy ();
NewAccountDialog.open (); NewAccountDialog.open ();
} }
} }
@ -73,14 +73,14 @@ public class Tootle.Accounts : Object{
return saved_accounts.length == 0; return saved_accounts.length == 0;
} }
public void init (){ public void init () {
save (false); save (false);
load (); load ();
if (saved_accounts.length < 1) if (saved_accounts.length < 1)
NewAccountDialog.open (); NewAccountDialog.open ();
else else
switch_account (Tootle.settings.current_account); switch_account (settings.current_account);
} }
private void save (bool overwrite = true) { private void save (bool overwrite = true) {

View File

@ -5,17 +5,17 @@ public class Tootle.WatchlistDialog : Gtk.Window {
private static WatchlistDialog dialog; private static WatchlistDialog dialog;
private Gtk.HeaderBar header; private HeaderBar header;
private Gtk.StackSwitcher switcher; private StackSwitcher switcher;
private Gtk.MenuButton button_add; private Gtk.MenuButton button_add;
private Gtk.Stack stack; private Stack stack;
private ListStack users; private ListStack users;
private ListStack hashtags; private ListStack hashtags;
private Gtk.Popover popover; private Popover popover;
private Gtk.Grid popover_grid; private Grid popover_grid;
private Gtk.Entry popover_entry; private Entry popover_entry;
private Gtk.Button popover_button; private Button popover_button;
private const string TIP_USERS = _("You'll be notified when toots from specific users appear in your Home timeline."); private const string TIP_USERS = _("You'll be notified when toots from specific users appear in your Home timeline.");
private const string TIP_HASHTAGS = _("You'll be notified when toots with specific hashtags are posted in any public timelines."); private const string TIP_HASHTAGS = _("You'll be notified when toots with specific hashtags are posted in any public timelines.");
@ -30,21 +30,21 @@ public class Tootle.WatchlistDialog : Gtk.Window {
} }
} }
private class ModelView : Gtk.ListBoxRow { private class ModelView : ListBoxRow {
private Gtk.Box box; private Box box;
private Gtk.Button button_remove; private Button button_remove;
private Gtk.Label label; private Label label;
private bool is_hashtag; private bool is_hashtag;
public ModelView (ModelItem item) { public ModelView (ModelItem item) {
is_hashtag = item.is_hashtag; is_hashtag = item.is_hashtag;
box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); box = new Box (Orientation.HORIZONTAL, 0);
box.margin = 6; box.margin = 6;
label = new Gtk.Label (item.name); label = new Label (item.name);
label.vexpand = true; label.vexpand = true;
label.valign = Gtk.Align.CENTER; label.valign = Align.CENTER;
label.justify = Gtk.Justification.LEFT; label.justify = Justification.LEFT;
button_remove = new Gtk.Button.from_icon_name ("list-remove-symbolic", Gtk.IconSize.BUTTON); button_remove = new Button.from_icon_name ("list-remove-symbolic", IconSize.BUTTON);
button_remove.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); button_remove.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT);
button_remove.clicked.connect (() => { button_remove.clicked.connect (() => {
watchlist.remove (label.label, is_hashtag); watchlist.remove (label.label, is_hashtag);
@ -85,19 +85,21 @@ public class Tootle.WatchlistDialog : Gtk.Window {
return new ModelView (item); return new ModelView (item);
} }
private class ListStack : Gtk.ScrolledWindow { private class ListStack : ScrolledWindow {
public Model model; public Model model;
public Gtk.ListBox list; public ListBox list;
private bool is_hashtags; private bool is_hashtags;
public void update () { public void update () {
if (is_hashtags) if (is_hashtags)
watchlist.hashtags.@foreach (item => { watchlist.hashtags.@foreach (item => {
model.append (new ModelItem (item, true)); model.append (new ModelItem (item, true));
return true;
}); });
else else
watchlist.users.@foreach (item => { watchlist.users.@foreach (item => {
model.append (new ModelItem (item, false)); model.append (new ModelItem (item, false));
return true;
}); });
list.bind_model (model, create_row); list.bind_model (model, create_row);
@ -106,7 +108,7 @@ public class Tootle.WatchlistDialog : Gtk.Window {
public ListStack (bool is_hashtags) { public ListStack (bool is_hashtags) {
this.is_hashtags = is_hashtags; this.is_hashtags = is_hashtags;
model = new Model (); model = new Model ();
list = new Gtk.ListBox (); list = new ListBox ();
add (list); add (list);
update (); update ();
} }
@ -122,8 +124,8 @@ public class Tootle.WatchlistDialog : Gtk.Window {
resizable = true; resizable = true;
transient_for = window; transient_for = window;
stack = new Gtk.Stack (); stack = new Stack ();
stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT; stack.transition_type = StackTransitionType.SLIDE_LEFT_RIGHT;
stack.hexpand = true; stack.hexpand = true;
stack.vexpand = true; stack.vexpand = true;
@ -134,37 +136,37 @@ public class Tootle.WatchlistDialog : Gtk.Window {
stack.add_titled (hashtags, "hashtags", _("Hashtags")); stack.add_titled (hashtags, "hashtags", _("Hashtags"));
stack.set_size_request (400, 300); stack.set_size_request (400, 300);
popover_entry = new Gtk.Entry (); popover_entry = new Entry ();
popover_entry.hexpand = true; popover_entry.hexpand = true;
popover_entry.secondary_icon_name = "dialog-information-symbolic"; popover_entry.secondary_icon_name = "dialog-information-symbolic";
popover_entry.secondary_icon_activatable = false; popover_entry.secondary_icon_activatable = false;
popover_entry.activate.connect (() => submit ()); popover_entry.activate.connect (() => submit ());
popover_button = new Gtk.Button.with_label (_("Add")); popover_button = new Button.with_label (_("Add"));
popover_button.halign = Gtk.Align.END; popover_button.halign = Align.END;
popover_button.margin_start = 6; popover_button.margin_start = 6;
popover_button.clicked.connect (() => submit ()); popover_button.clicked.connect (() => submit ());
popover_grid = new Gtk.Grid (); popover_grid = new Grid ();
popover_grid.margin = 6; popover_grid.margin = 6;
popover_grid.attach (popover_entry, 0, 0); popover_grid.attach (popover_entry, 0, 0);
popover_grid.attach (popover_button, 1, 0); popover_grid.attach (popover_button, 1, 0);
popover_grid.show_all (); popover_grid.show_all ();
popover = new Gtk.Popover (null); popover = new Popover (null);
popover.add (popover_grid); popover.add (popover_grid);
button_add = new Gtk.MenuButton (); button_add = new MenuButton ();
button_add.image = new Gtk.Image.from_icon_name ("list-add-symbolic", Gtk.IconSize.BUTTON); button_add.image = new Image.from_icon_name ("list-add-symbolic", IconSize.BUTTON);
button_add.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION); button_add.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
button_add.popover = popover; button_add.popover = popover;
button_add.clicked.connect (() => set_tip ()); button_add.clicked.connect (() => set_tip ());
switcher = new StackSwitcher (); switcher = new StackSwitcher ();
switcher.stack = stack; switcher.stack = stack;
switcher.halign = Gtk.Align.CENTER; switcher.halign = Align.CENTER;
header = new Gtk.HeaderBar (); header = new HeaderBar ();
header.show_close_button = true; header.show_close_button = true;
header.pack_start (button_add); header.pack_start (button_add);
header.set_custom_title (switcher); header.set_custom_title (switcher);

View File

@ -101,6 +101,7 @@ public class Tootle.InstanceAccount : GLib.Object {
watchlist.users.@foreach (item => { watchlist.users.@foreach (item => {
if (item == acct || item == "@" + acct) if (item == acct || item == "@" + acct)
notification (obj); notification (obj);
return true;
}); });
} }

View File

@ -238,16 +238,16 @@ public class Tootle.AccountView : TimelineView {
public static void open_from_id (int64 id){ public static void open_from_id (int64 id){
var url = "%s/api/v1/accounts/%lld".printf (accounts.formal.instance, id); var url = "%s/api/v1/accounts/%lld".printf (accounts.formal.instance, id);
var msg = new Soup.Message("GET", url); var msg = new Soup.Message ("GET", url);
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
network.queue (msg, (sess, mess) => { network.queue (msg, (sess, mess) => {
try{ try {
var root = network.parse (mess); var root = network.parse (mess);
var acc = Account.parse (root); var acc = Account.parse (root);
Tootle.window.open_view (new AccountView (acc)); window.open_view (new AccountView (acc));
} }
catch (GLib.Error e) { catch (GLib.Error e) {
warning ("Can't update feed"); warning ("Can't find account");
warning (e.message); warning (e.message);
} }
}); });
@ -258,7 +258,7 @@ public class Tootle.AccountView : TimelineView {
var msg = new Soup.Message("GET", url); var msg = new Soup.Message("GET", url);
msg.priority = Soup.MessagePriority.HIGH; msg.priority = Soup.MessagePriority.HIGH;
network.queue (msg, (sess, mess) => { network.queue (msg, (sess, mess) => {
try{ try {
var node = network.parse_array (mess).get_element (0); var node = network.parse_array (mess).get_element (0);
var object = node.get_object (); var object = node.get_object ();
if (object != null){ if (object != null){

View File

@ -1,13 +1,12 @@
using Soup;
using GLib; using GLib;
using Gdk; using Gdk;
using Json; using Gee;
public class Tootle.Watchlist : GLib.Object { public class Tootle.Watchlist : Object {
public GenericArray<string> users = new GenericArray<string> (); public ArrayList<string> users = new ArrayList<string> ();
public GenericArray<string> hashtags = new GenericArray<string> (); public ArrayList<string> hashtags = new ArrayList<string> ();
public GenericArray<Notificator> notificators = new GenericArray<Notificator> (); public ArrayList<Notificator> notificators = new ArrayList<Notificator> ();
construct { construct {
accounts.switched.connect (on_account_changed); accounts.switched.connect (on_account_changed);
@ -16,21 +15,23 @@ public class Tootle.Watchlist : GLib.Object {
public Watchlist () {} public Watchlist () {}
public virtual void on_account_changed (Account? account){ public virtual void on_account_changed (Account? account){
if(account != null) if (account != null)
reload (); reload ();
} }
private void reload () { private void reload () {
info ("Reloading"); info ("Reloading");
notificators.@foreach (notificator => notificator.close ()); notificators.@foreach (notificator => {
notificators.remove_range (0, notificators.length); notificator.close ();
users.remove_range (0, users.length); return true;
hashtags.remove_range (0, hashtags.length); });
notificators.clear ();
users.clear ();
hashtags.clear ();
load (); load ();
info ("Watching for %i users and %i hashtags", users.size, hashtags.size);
info ("Watching for %i users and %i hashtags", users.length, hashtags.length);
} }
private void load () { private void load () {
@ -45,12 +46,18 @@ public class Tootle.Watchlist : GLib.Object {
public void save () { public void save () {
var serialized_users = ""; var serialized_users = "";
users.@foreach (item => serialized_users += item + ","); users.@foreach (item => {
serialized_users += item + ",";
return true;
});
serialized_users = remove_last_delimiter (serialized_users); serialized_users = remove_last_delimiter (serialized_users);
settings.watched_users = serialized_users; settings.watched_users = serialized_users;
var serialized_hashtags = ""; var serialized_hashtags = "";
hashtags.@foreach (item => serialized_hashtags += item + ","); hashtags.@foreach (item => {
serialized_hashtags += item + ",";
return true;
});
serialized_hashtags = remove_last_delimiter (serialized_hashtags); serialized_hashtags = remove_last_delimiter (serialized_hashtags);
settings.watched_hashtags = serialized_hashtags; settings.watched_hashtags = serialized_hashtags;
@ -99,26 +106,21 @@ public class Tootle.Watchlist : GLib.Object {
} }
public void remove (string entity, bool is_hashtag) { public void remove (string entity, bool is_hashtag) {
int i = -1; if (entity == "")
if (is_hashtag) return;
hashtags.@foreach (item => {
i++; if (is_hashtag) {
if (item == entity) { var i = hashtags.index_of (entity);
var notificator = notificators.@get(i); var notificator = notificators.@get(i);
notificator.close (); notificator.close ();
notificators.remove_index (i); notificators.remove_at (i);
hashtags.remove_index (i); hashtags.remove (entity);
info ("Removed #%s", entity); info ("Removed #%s", entity);
} }
}); else {
else users.remove (entity);
users.@foreach (item => { info ("Removed @%s", entity);
i++; }
if (item == entity) {
users.remove_index (i);
info ("Removed @%s", entity);
}
});
} }
} }

View File

@ -14,7 +14,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
Gtk.ModelButton item_direct; Gtk.ModelButton item_direct;
Gtk.ModelButton item_watchlist; Gtk.ModelButton item_watchlist;
private class AccountView : Gtk.ListBoxRow{ private class AccountItemView : Gtk.ListBoxRow{
private Gtk.Grid grid; private Gtk.Grid grid;
public Gtk.Label display_name; public Gtk.Label display_name;
@ -46,7 +46,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
show_all (); show_all ();
} }
public AccountView (){ public AccountItemView (){
button.clicked.connect (() => accounts.remove (id)); button.clicked.connect (() => accounts.remove (id));
} }
@ -106,21 +106,23 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
get_style_context ().add_class ("button_avatar"); get_style_context ().add_class ("button_avatar");
popover = menu; popover = menu;
add(avatar); add (avatar);
show_all (); show_all ();
accounts.updated.connect (accounts_updated); accounts.updated.connect (accounts_updated);
accounts.switched.connect (account_switched); accounts.switched.connect (account_switched);
list.row_activated.connect (row => { list.row_activated.connect (row => {
var widget = row as AccountView; var widget = row as AccountItemView;
if (widget.id == -1) { if (widget.id == -1) {
NewAccountDialog.open (); NewAccountDialog.open ();
return; return;
} }
if (widget.id == Tootle.settings.current_account) if (widget.id == settings.current_account)
return; AccountView.open_from_id (accounts.current.id);
else else
accounts.switch_account (widget.id); accounts.switch_account (widget.id);
menu.popdown ();
}); });
} }
@ -129,14 +131,14 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
int i = -1; int i = -1;
accounts.foreach (account => { accounts.foreach (account => {
i++; i++;
var widget = new AccountView (); var widget = new AccountItemView ();
widget.id = i; widget.id = i;
widget.display_name.label = "<b>@"+account.username+"</b>"; widget.display_name.label = "<b>@"+account.username+"</b>";
widget.instance.label = account.get_pretty_instance (); widget.instance.label = account.get_pretty_instance ();
list.add (widget); list.add (widget);
}); });
var add_account = new AccountView (); var add_account = new AccountItemView ();
add_account.display_name.label = _("<b>New Account</b>"); add_account.display_name.label = _("<b>New Account</b>");
add_account.instance.label = _("Click to add"); add_account.instance.label = _("Click to add");
add_account.button.hide (); add_account.button.hide ();
@ -152,7 +154,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{
} }
private void update_selection () { private void update_selection () {
var id = Tootle.settings.current_account; var id = settings.current_account;
var row = list.get_row_at_index (id); var row = list.get_row_at_index (id);
if (row != null) if (row != null)
list.select_row (row); list.select_row (row);