Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions data/gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<file>ui/views/base.ui</file>
<file>ui/views/profile_header.ui</file>
<file>ui/widgets/status.ui</file>
<file>ui/widgets/votebox.ui</file>
<file>ui/widgets/accounts_button.ui</file>
<file>ui/widgets/accounts_button_item.ui</file>
<file>ui/widgets/profile_field_row.ui</file>
Expand Down
198 changes: 115 additions & 83 deletions data/ui/widgets/status.ui

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions data/ui/widgets/votebox.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<template class="TootleWidgetsVoteBox" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="pollBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_vote">
<property name="label" translatable="yes">Vote</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</template>
</interface>
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ executable(
'src/API/AccountField.vala',
'src/API/Relationship.vala',
'src/API/Mention.vala',
'src/API/Poll.vala',
'src/API/PollOption.vala',
'src/API/Tag.vala',
'src/API/Status.vala',
'src/API/Visibility.vala',
Expand All @@ -96,6 +98,7 @@ executable(
'src/Widgets/Attachment/Slot.vala',
'src/Widgets/Attachment/Picture.vala',
'src/Widgets/AdaptiveButton.vala',
'src/Widgets/VoteBox.vala',
'src/Dialogs/ISavedWindow.vala',
'src/Dialogs/NewAccount.vala',
'src/Dialogs/MainWindow.vala',
Expand Down
8 changes: 8 additions & 0 deletions po/com.github.bleakgrey.tootle.pot
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,14 @@ msgstr ""
msgid "Delete"
msgstr ""

#: src/Widgets/VoteBox.vala:17
msgid "Vote"
msgstr ""

#: src/Widgets/VoteBox.vala:66
msgid "Votes: %s"
msgstr ""

#: data/ui/widgets/list_item.ui:26 src/Dialogs/ListEditor.vala:87
msgid "Untitled"
msgstr ""
Expand Down
4 changes: 3 additions & 1 deletion src/API/Entity.vala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
return success;
}

static bool des_list (out Value val, Json.Node node, Type type) {
public static bool des_list (out Value val, Json.Node node, Type type) {
var tipo=node.type_name();
if (!node.is_null ()) {
var arr = new Gee.ArrayList<Entity> ();
node.get_array ().foreach_element ((array, i, elem) => {
Expand Down Expand Up @@ -137,3 +138,4 @@ public class Tootle.Entity : GLib.Object, Widgetizable, Json.Serializable {
}

}

85 changes: 85 additions & 0 deletions src/API/Poll.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using Gee;
using Json;
public class Tootle.API.Poll : GLib.Object, Json.Serializable{
public string id { get; set; }
public string expires_at{ get; set; }
public bool expired { get; set; }
public bool multiple { get; set; }
public int64 votes_count { get; set; }
public int64 voters_count { get; set; }
public bool voted { get; set; default = true;}
public ArrayList<int> own_votes { get; set; }
public ArrayList<PollOption>? options{ get; set; default = null; }

public Poll (string _id) {
id = _id;
}

public override bool deserialize_property (string prop, out Value val, ParamSpec spec, Json.Node node) {
var success = default_deserialize_property (prop, out val, spec, node);

var type = spec.value_type;
if (prop=="options"){
return Entity.des_list (out val, node, typeof (API.PollOption));
}
if (prop=="own-votes"){
return Poll.des_list_int (out val, node);
}

if (val.type () == Type.INVALID) { // Fix for glib-json < 1.5.1
val.init (type);
spec.set_value_default (ref val);
type = spec.value_type;
}

return success;
}
public static bool des_list_int (out Value val, Json.Node node) {
if (!node.is_null ()) {
var arr = new Gee.ArrayList<int> ();
node.get_array ().foreach_element ((array, i, elem) => {
arr.add ((int)elem.get_int());
});
val = arr;
}
return true;
}
public static Poll from_json (Type type, Json.Node? node) throws Oopsie {
if (node == null)
throw new Oopsie.PARSING (@"Received Json.Node for $(type.name ()) is null!");

var obj = node.get_object ();
if (obj == null)
throw new Oopsie.PARSING (@"Received Json.Node for $(type.name ()) is not a Json.Object!");

return Json.gobject_deserialize (type, node) as Poll;
}
public static Request vote (InstanceAccount acc,ArrayList<PollOption> options,ArrayList<string> selection, string id) {
message (@"Voting poll $(id)...");
//Creating json to send
var builder = new Json.Builder ();
builder.begin_object ();
builder.set_member_name ("choices");
builder.begin_array ();
var row_number=0;
foreach (API.PollOption p in options){
foreach (string select in selection){
if (select == p.title){
builder.add_string_value (row_number.to_string());
}
}
row_number++;
}
builder.end_array ();
builder.end_object ();
var generator = new Json.Generator ();
generator.set_root (builder.get_root ());
var json = generator.to_data (null);
//Send POST MESSAGE
Request voting=new Request.POST (@"/api/v1/polls/$(id)/votes")
.with_account (acc);
voting.set_request("application/json",Soup.MemoryUse.COPY,json.data);
return voting;
}

}
4 changes: 4 additions & 0 deletions src/API/PollOption.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class Tootle.API.PollOption: Entity {
public string? title { get; set; }
public int64 votes_count{ get; set; }
}
3 changes: 3 additions & 0 deletions src/API/Status.vala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class Tootle.API.Status : Entity, Widgetizable {
public API.Status? reblog { get; set; default = null; }
public ArrayList<API.Mention>? mentions { get; set; default = null; }
public ArrayList<API.Attachment>? media_attachments { get; set; default = null; }
public API.Poll? poll { get; set; default = null; }


public string? _url { get; set; }
public string url {
Expand Down Expand Up @@ -126,3 +128,4 @@ public class Tootle.API.Status : Entity, Widgetizable {
}

}

8 changes: 8 additions & 0 deletions src/Widgets/Status.vala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public class Tootle.Widgets.Status : ListBoxRow {
[GtkChild] protected ToggleButton bookmark_button;
[GtkChild] protected Button menu_button;

[GtkChild] protected Widgets.VoteBox poll;


protected string spoiler_text {
owned get {
var text = status.formal.spoiler_text;
Expand Down Expand Up @@ -176,6 +179,10 @@ public class Tootle.Widgets.Status : ListBoxRow {
}

menu_button.clicked.connect (open_menu);

status.bind_property ("poll", poll, "poll", BindingFlags.SYNC_CREATE);
status.bind_property ("account", poll, "account", BindingFlags.SYNC_CREATE);

}

public Status (API.Status status, API.NotificationType? kind = null) {
Expand Down Expand Up @@ -296,3 +303,4 @@ public class Tootle.Widgets.Status : ListBoxRow {
}

}

143 changes: 143 additions & 0 deletions src/Widgets/VoteBox.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using Gtk;
using Gdk;
using Gee;

[GtkTemplate (ui = "/com/github/bleakgrey/tootle/ui/widgets/votebox.ui")]
public class Tootle.Widgets.VoteBox: Box {
[GtkChild] protected Gtk.Box pollBox;
[GtkChild] protected Gtk.Button button_vote;

public API.Poll? poll { get; set;}
public API.Account? account { get; set;}

protected ArrayList<string> selectedIndex=new ArrayList<string>();

construct{

button_vote.set_label (_("Vote"));
button_vote.clicked.connect ((button) =>{
Request voting=API.Poll.vote(accounts.active,poll.options,selectedIndex,poll.id);
voting.then ((sess, mess) => {
var node = network.parse_node (mess);
var poll_updated=API.Poll.from_json(typeof(API.Poll),node);
poll.expired=poll_updated.expired;
poll.votes_count=poll_updated.votes_count;
poll.voters_count=poll_updated.voters_count;
poll.voted=poll_updated.voted;
poll.own_votes=poll_updated.own_votes;
poll.options=poll_updated.options;
update();
message ("OK: Voting correctly");
})
.on_error ((code, reason) => {
warning ("Voting invalid!");
app.error (
_("Network Error"),
_("The instance has invalidated this session. Please sign in again.\n\n%s").printf (reason)
);
}).exec ();
});
notify["poll"].connect (update);
notify["account"].connect (update);
update();
}

void update(){
if (poll==null || account == null)
{
button_vote.hide();
return;
}
GLib.List<weak Gtk.Widget> children=pollBox.get_children();
foreach (Widget child in children){
pollBox.remove(child);
}
var row_number=0;
Gtk.RadioButton[] radios={};
Gtk.CheckButton[] checks={};
if (poll.own_votes.size==0 && !poll.multiple){
var element=poll.options.get(0);
selectedIndex.add(element.title);
}
foreach (API.PollOption p in poll.options){
//if it is own poll
if(account.id==accounts.active.id){
// If multiple, Checkbox else radioButton
var option = new Widgets.RichLabel (p.title+" "+_("Votes: %s".printf ((p.votes_count).to_string())));
pollBox.add(option);
}
else{
// If multiple, Checkbox else radioButton
if (poll.multiple){
var check_option = new Gtk.CheckButton ();
check_option.set_label(p.title);
check_option.toggled.connect((radio)=>{
if (selectedIndex.contains(radio.get_label())){
selectedIndex.remove(radio.get_label());
}
else{
selectedIndex.add(radio.get_label());
}
});
foreach (int own_vote in poll.own_votes){
if (own_vote==row_number){
check_option.set_active(true);
if (!selectedIndex.contains(p.title)){
selectedIndex.add(p.title);
}
}
}
if(poll.expired || poll.voted){
check_option.set_sensitive(false);
}
pollBox.add(check_option);
checks+=check_option;
}else{
//If not multiple, chose RadioButton
Gtk.RadioButton radio_option = null;
if (radios.length==0){
radio_option=new Gtk.RadioButton (null);
}
else{
radio_option=new Gtk.RadioButton (radios[0].get_group());
}
radio_option.set_label(p.title);
radio_option.toggled.connect((radiobutton)=>{
if (selectedIndex.contains(radiobutton.get_label()))
{
selectedIndex.remove(radiobutton.get_label());
}
else{
selectedIndex.add(radiobutton.get_label());
}
});

foreach (int own_vote in poll.own_votes){
if (own_vote==row_number){
radio_option.set_active(true);
selectedIndex=new ArrayList<string>();
if (!selectedIndex.contains(p.title)){
selectedIndex.add(p.title);
}
}
}
if(poll.expired || poll.voted){
radio_option.set_sensitive(false);
}
pollBox.add(radio_option);
radios+=radio_option;
}
}
row_number++;
}
if(row_number>0 && !poll.expired && !poll.voted &&
account.id!=accounts.active.id ){
button_vote.show();
}
else{
button_vote.hide();
}
pollBox.show_all();
}

}