Compare commits
No commits in common. "106d4d0ed8bd152315a085fd80244645bb77ec3d" and "04f8e4dbdb68ef8acee9fe0e14c5d570322f70db" have entirely different histories.
106d4d0ed8
...
04f8e4dbdb
3 changed files with 99 additions and 94 deletions
129
MainWindow.cs
129
MainWindow.cs
|
@ -26,16 +26,26 @@ using System.Threading.Tasks;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Buypeeb {
|
namespace Buypeeb {
|
||||||
|
public struct ItemColumns {
|
||||||
|
public const int Name = 0;
|
||||||
|
public const int PriceYen = 1;
|
||||||
|
public const int PriceAUD = 2;
|
||||||
|
public const int Ending = 3;
|
||||||
|
public const int Id = 4;
|
||||||
|
}
|
||||||
|
|
||||||
class MainWindow : Window {
|
class MainWindow : Window {
|
||||||
|
|
||||||
private string location;
|
private string location;
|
||||||
|
// private Queue<string> statuses;
|
||||||
|
|
||||||
private ListStore items;
|
private ListStore items;
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
private TreeView itemTreeView;
|
private TreeView itemTreeView;
|
||||||
|
private Label statusLabel;
|
||||||
private Builder builder;
|
private Builder builder;
|
||||||
|
|
||||||
// TODO: whenever we get something from the builder, cache it for later
|
// TODO: whenever we get something from the builder, cache it for later
|
||||||
|
@ -44,12 +54,10 @@ namespace Buypeeb {
|
||||||
|
|
||||||
private Box selectionViewBox;
|
private Box selectionViewBox;
|
||||||
private Label endingLabel;
|
private Label endingLabel;
|
||||||
private bool queueActive;
|
|
||||||
|
|
||||||
// ...to here.
|
// ...to here.
|
||||||
|
|
||||||
static SemaphoreSlim taskLimit = new SemaphoreSlim(6);
|
static SemaphoreSlim taskLimit = new SemaphoreSlim(4);
|
||||||
private Queue<string> updateQueue = new Queue<string>();
|
|
||||||
|
|
||||||
private YahooAuctionsItem selectedItem {
|
private YahooAuctionsItem selectedItem {
|
||||||
get {
|
get {
|
||||||
|
@ -97,6 +105,7 @@ namespace Buypeeb {
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
builder.Autoconnect(this);
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
|
this.statusLabel = (Label)builder.GetObject("LabelStatus");
|
||||||
this.selectionViewBox = (Box)builder.GetObject("SelectionViewBox");
|
this.selectionViewBox = (Box)builder.GetObject("SelectionViewBox");
|
||||||
this.endingLabel = (Label)builder.GetObject("LabelSelectedEnding");
|
this.endingLabel = (Label)builder.GetObject("LabelSelectedEnding");
|
||||||
|
|
||||||
|
@ -107,7 +116,6 @@ namespace Buypeeb {
|
||||||
|
|
||||||
this.itemTreeView.Model = this.items;
|
this.itemTreeView.Model = this.items;
|
||||||
TreeCellDataFunc[] funcs = {
|
TreeCellDataFunc[] funcs = {
|
||||||
new TreeCellDataFunc(this.RenderColumnFavourite),
|
|
||||||
new TreeCellDataFunc(this.RenderColumnName),
|
new TreeCellDataFunc(this.RenderColumnName),
|
||||||
new TreeCellDataFunc(this.RenderColumnPriceYen),
|
new TreeCellDataFunc(this.RenderColumnPriceYen),
|
||||||
new TreeCellDataFunc(this.RenderColumnPriceAUD),
|
new TreeCellDataFunc(this.RenderColumnPriceAUD),
|
||||||
|
@ -133,6 +141,10 @@ namespace Buypeeb {
|
||||||
|
|
||||||
// general behaviour
|
// general behaviour
|
||||||
|
|
||||||
|
private void SetStatus(string status) {
|
||||||
|
this.statusLabel.Text = status ?? "Buypeeb";
|
||||||
|
}
|
||||||
|
|
||||||
private (TreePath path, TreeIter iter) GetRow(string id) {
|
private (TreePath path, TreeIter iter) GetRow(string id) {
|
||||||
// TODO: surely there's a better way to do this
|
// TODO: surely there's a better way to do this
|
||||||
TreeIter iter;
|
TreeIter iter;
|
||||||
|
@ -168,7 +180,7 @@ namespace Buypeeb {
|
||||||
|
|
||||||
private void UpdateThread(string id) {
|
private void UpdateThread(string id) {
|
||||||
var item = this.settings.watchlist[id];
|
var item = this.settings.watchlist[id];
|
||||||
// Console.WriteLine($"Updating {id}...");
|
Console.WriteLine($"Updating {id}...");
|
||||||
// set item.ready to false to show that it's still being updated
|
// set item.ready to false to show that it's still being updated
|
||||||
// this changes a few behaviours, such as displaying the price as "..." instead of whatever's currently stored
|
// this changes a few behaviours, such as displaying the price as "..." instead of whatever's currently stored
|
||||||
item.ready = false;
|
item.ready = false;
|
||||||
|
@ -195,31 +207,17 @@ namespace Buypeeb {
|
||||||
});
|
});
|
||||||
|
|
||||||
item.ready = true;
|
item.ready = true;
|
||||||
// Console.WriteLine($"{id} updated.");
|
Console.WriteLine($"{id} updated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessUpdateQueue() {
|
private void UpdateItem(string id, bool force = false) {
|
||||||
// recursively process the updatequeue
|
// don't start a new task if there are more than [tasklimit] tasks currently running
|
||||||
// this is a BLOCKING FUNCTION
|
// this makes sure we don't make 1000 simultaneous requests to yahoo auctions if there are 1000 items on the watchlist
|
||||||
this.queueActive = true;
|
if (this.settings.watchlist[id].updatedRecently && !force) {
|
||||||
this.UpdateItem(this.updateQueue.Dequeue());
|
|
||||||
if (this.updateQueue.TryPeek(out string _)) {
|
|
||||||
this.ProcessUpdateQueue();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.queueActive = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateItem(string id, bool evenIfAlreadyUpdating = false, bool force = false) {
|
|
||||||
var item = this.settings.watchlist[id];
|
|
||||||
if (item.updatedRecently && !force) {
|
|
||||||
// the item has been updated recently, and force is not true
|
// the item has been updated recently, and force is not true
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't start a new task if there are more than `tasklimit` tasks currently running
|
|
||||||
// this makes sure we don't make 1000 simultaneous requests to yahoo auctions if there are 1000 items on the watchlist
|
|
||||||
taskLimit.Wait();
|
taskLimit.Wait();
|
||||||
var t = Task.Factory.StartNew(() => {
|
var t = Task.Factory.StartNew(() => {
|
||||||
this.UpdateThread(id);
|
this.UpdateThread(id);
|
||||||
|
@ -227,28 +225,12 @@ namespace Buypeeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateItems() {
|
private void UpdateItems() {
|
||||||
if (this.queueActive) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectionViewBox.Sensitive = false;
|
this.selectionViewBox.Sensitive = false;
|
||||||
foreach (var item in this.settings.watchlist) {
|
|
||||||
if (!this.updateQueue.Contains(item.Key)) {
|
|
||||||
// set everything to not ready first
|
|
||||||
// ensures other actions don't attempt to display the items even if UpdateItem hasn't started yet
|
|
||||||
item.Value.ready = false;
|
|
||||||
this.updateQueue.Enqueue(item.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.updateQueue.TryPeek(out string _)) {
|
|
||||||
// queue is empty
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.itemTreeView.QueueDraw();
|
|
||||||
var t = Task.Factory.StartNew(() => {
|
var t = Task.Factory.StartNew(() => {
|
||||||
this.ProcessUpdateQueue();
|
foreach (var item in this.settings.watchlist) {
|
||||||
|
this.UpdateItem(item.Key);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -269,19 +251,20 @@ namespace Buypeeb {
|
||||||
this.selectionViewBox.Sensitive = item.ready;
|
this.selectionViewBox.Sensitive = item.ready;
|
||||||
infobox.Visible = true;
|
infobox.Visible = true;
|
||||||
|
|
||||||
var info = new Dictionary<string, string> {
|
var info = new Dictionary<string, string>
|
||||||
{ "Name", item.name },
|
{
|
||||||
{ "YahooName", item.originalName },
|
{ "Name", item.name },
|
||||||
{ "Price", item.PriceJPY() },
|
{ "YahooName", item.originalName },
|
||||||
{ "PriceAUD", item.PriceAUD() },
|
{ "Price", item.PriceJPY() },
|
||||||
{ "Ending", "..." },
|
{ "PriceAUD", item.PriceAUD() },
|
||||||
{ "Bids", $"{item.bids}" },
|
{ "Ending", "..." },
|
||||||
{ "BuyItNow", item.winPrice == 0 ? "No" : $"¥{item.PriceJPY(true)} (${item.PriceAUD(true)})" },
|
{ "Bids", $"{item.bids}" },
|
||||||
{ "AutoExtension", item.autoExtension ? "Yes" : "No" },
|
{ "BuyItNow", item.winPrice == 0 ? "No" : $"¥{item.PriceJPY(true)} (${item.PriceAUD(true)})" },
|
||||||
{ "LastUpdated", "Last updated: heeeenlo" }
|
{ "AutoExtension", item.autoExtension ? "Yes" : "No" },
|
||||||
};
|
{ "LastUpdated", "Last updated: heeeenlo" }
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var row in info) {
|
foreach (var row in info) {
|
||||||
var l = (Label)this.builder.GetObject($"LabelSelected{row.Key}");
|
var l = (Label)this.builder.GetObject($"LabelSelected{row.Key}");
|
||||||
l.Text = row.Value;
|
l.Text = row.Value;
|
||||||
}
|
}
|
||||||
|
@ -392,18 +375,18 @@ namespace Buypeeb {
|
||||||
md.Dispose();
|
md.Dispose();
|
||||||
if (r != ResponseType.Ok) {
|
if (r != ResponseType.Ok) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeMe = new List<string>();
|
var removeMe = new List<string>();
|
||||||
foreach (var item in this.settings.watchlist) {
|
foreach(var item in this.settings.watchlist) {
|
||||||
if (!item.Value.available) {
|
if (!item.Value.available) {
|
||||||
removeMe.Add(item.Key);
|
removeMe.Add(item.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in removeMe) {
|
foreach(var id in removeMe) {
|
||||||
this.settings.watchlist.Remove(id);
|
this.settings.watchlist.Remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.RenderList();
|
this.RenderList();
|
||||||
}
|
}
|
||||||
|
@ -463,20 +446,12 @@ namespace Buypeeb {
|
||||||
|
|
||||||
private void ButtonSelectedUpdateClicked(object sender, EventArgs args) {
|
private void ButtonSelectedUpdateClicked(object sender, EventArgs args) {
|
||||||
this.selectionViewBox.Sensitive = false;
|
this.selectionViewBox.Sensitive = false;
|
||||||
if (this.updateQueue.Contains(this.selectedItem.id)) {
|
|
||||||
// the item is already waiting to be updated
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.UpdateItem(this.selectedItem.id);
|
this.UpdateItem(this.selectedItem.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ButtonSelectedFavouriteToggled(object sender, EventArgs args) {
|
private void ButtonSelectedFavouriteToggled(object sender, EventArgs args) {
|
||||||
ToggleButton s = (ToggleButton)sender;
|
ToggleButton s = (ToggleButton)sender;
|
||||||
this.selectedItem.favourite = s.Active;
|
this.selectedItem.favourite = s.Active;
|
||||||
|
|
||||||
// i don't know why this is necessary
|
|
||||||
var pathAndIter = this.GetRow(this.selectedItem.id);
|
|
||||||
this.items.EmitRowChanged(pathAndIter.path, pathAndIter.iter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// timers
|
// timers
|
||||||
|
@ -502,7 +477,7 @@ namespace Buypeeb {
|
||||||
}
|
}
|
||||||
// timespan objects don't contain definitions for the time or date separators, so the colons need to be escaped
|
// timespan objects don't contain definitions for the time or date separators, so the colons need to be escaped
|
||||||
// `HH` doesn't exist, but `hh` behaves identically
|
// `HH` doesn't exist, but `hh` behaves identically
|
||||||
// see https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings
|
// see https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings
|
||||||
ending += span.ToString(@"hh\:mm\:ss");
|
ending += span.ToString(@"hh\:mm\:ss");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -515,11 +490,6 @@ namespace Buypeeb {
|
||||||
|
|
||||||
// column renderers
|
// column renderers
|
||||||
|
|
||||||
private void RenderColumnFavourite(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) {
|
|
||||||
YahooAuctionsItem item = (YahooAuctionsItem)model.GetValue(iter, 0);
|
|
||||||
(cell as Gtk.CellRendererText).Text = item.favourite ? "♥" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderColumnName(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) {
|
private void RenderColumnName(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.ITreeModel model, Gtk.TreeIter iter) {
|
||||||
YahooAuctionsItem item = (YahooAuctionsItem)model.GetValue(iter, 0);
|
YahooAuctionsItem item = (YahooAuctionsItem)model.GetValue(iter, 0);
|
||||||
(cell as Gtk.CellRendererText).Text = item.name ?? "Loading...";
|
(cell as Gtk.CellRendererText).Text = item.name ?? "Loading...";
|
||||||
|
@ -541,17 +511,18 @@ namespace Buypeeb {
|
||||||
if (item.ready) {
|
if (item.ready) {
|
||||||
if (!item.available) {
|
if (!item.available) {
|
||||||
ending = "Ended";
|
ending = "Ended";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
var end = item.endDate.ToLocalTime();
|
var end = item.endDate.ToLocalTime();
|
||||||
// TODO: should we show the year if the auction ends next year? 0uo
|
// TODO: should we show the year if the auction ends next year? 0uo
|
||||||
if (end.DayOfYear != now.DayOfYear) {
|
if (end.DayOfYear != now.DayOfYear)
|
||||||
|
{
|
||||||
// the auction isn't ending today, so we should show the day it's ending on for clarity
|
// the auction isn't ending today, so we should show the day it's ending on for clarity
|
||||||
ending += end.ToString("MMM d ");
|
ending += end.ToString("MMM d ");
|
||||||
}
|
}
|
||||||
ending += end.ToString("HH:mm");
|
ending += end.ToString("HH:mm");
|
||||||
if (this.settings.displaySecondsInList) {
|
if (this.settings.displaySecondsInList)
|
||||||
|
{
|
||||||
// add the seconds on to the end
|
// add the seconds on to the end
|
||||||
ending += end.ToString(":ss");
|
ending += end.ToString(":ss");
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ namespace Buypeeb {
|
||||||
public YahooAuctionsItem(string id, string name) {
|
public YahooAuctionsItem(string id, string name) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.ready = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public YahooAuctionsItem() {
|
public YahooAuctionsItem() {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.36.0 -->
|
<!-- Generated with glade 3.22.2 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<object class="GtkTextBuffer" id="TextBufferSelectedNotes"/>
|
<object class="GtkTextBuffer" id="TextBufferSelectedNotes"/>
|
||||||
<object class="GtkWindow" id="wndMain">
|
<object class="GtkWindow" id="wndMain">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="default_width">810</property>
|
<property name="default_width">700</property>
|
||||||
|
<child type="titlebar">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -112,6 +115,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton" id="ButtonClearEnded">
|
<object class="GtkToolButton" id="ButtonClearEnded">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Clear ended</property>
|
<property name="tooltip_text" translatable="yes">Clear ended</property>
|
||||||
<property name="label" translatable="yes">Clear ended</property>
|
<property name="label" translatable="yes">Clear ended</property>
|
||||||
|
@ -168,6 +172,7 @@
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkToolButton">
|
<object class="GtkToolButton">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Save</property>
|
<property name="tooltip_text" translatable="yes">Save</property>
|
||||||
<property name="label" translatable="yes">Save</property>
|
<property name="label" translatable="yes">Save</property>
|
||||||
|
@ -282,19 +287,11 @@
|
||||||
<signal name="changed" handler="TreeViewItemsSelectionChanged" swapped="no"/>
|
<signal name="changed" handler="TreeViewItemsSelectionChanged" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkTreeViewColumn">
|
|
||||||
<property name="title" translatable="yes">♥</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkCellRendererText"/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTreeViewColumn">
|
<object class="GtkTreeViewColumn">
|
||||||
<property name="resizable">True</property>
|
<property name="resizable">True</property>
|
||||||
<property name="sizing">fixed</property>
|
<property name="sizing">fixed</property>
|
||||||
<property name="fixed_width">100</property>
|
<property name="fixed_width">50</property>
|
||||||
<property name="title" translatable="yes">Name</property>
|
<property name="title" translatable="yes">Name</property>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="clickable">True</property>
|
<property name="clickable">True</property>
|
||||||
|
@ -610,7 +607,6 @@
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="margin_bottom">5</property>
|
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="spacing">5</property>
|
<property name="spacing">5</property>
|
||||||
<child>
|
<child>
|
||||||
|
@ -811,10 +807,47 @@
|
||||||
<property name="position">1</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_start">5</property>
|
||||||
|
<property name="margin_end">5</property>
|
||||||
|
<property name="margin_top">3</property>
|
||||||
|
<property name="margin_bottom">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="LabelStatus">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="label" translatable="yes">Tracking 3 items</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinner">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</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">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
Loading…
Reference in a new issue