Compare commits

..

2 commits

4 changed files with 469 additions and 331 deletions

View file

@ -11,6 +11,13 @@ namespace Buypeeb {
return $"https://page.auctions.yahoo.co.jp/jp/auction/{this.id}"; return $"https://page.auctions.yahoo.co.jp/jp/auction/{this.id}";
} }
} }
public string buyee_url {
get {
return $"https://buyee.jp/item/yahoo/auction/{this.id}";
}
}
public string id { get; set; } public string id { get; set; }
public string name { get; set; } public string name { get; set; }
public int price = 0; public int price = 0;
@ -72,13 +79,13 @@ namespace Buypeeb {
} }
} }
public string PriceAUD() { public string PriceAUD(bool win = false) {
double aud = this.price / 75.0; double aud = win ? this.win_price / 75.0 : this.price / 75.0;
return $"${aud:f2}"; return $"${aud:f2}";
} }
public string PriceJPY() { public string PriceJPY(bool win = false) {
return $"¥{this.price}"; return win ? $"¥{this.win_price}" : $"¥{this.price}";
} }
} }
} }

View file

@ -24,6 +24,7 @@ using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net; using System.Net;
using System.Diagnostics;
using Gtk; using Gtk;
namespace Buypeeb { namespace Buypeeb {
@ -44,9 +45,20 @@ namespace Buypeeb {
private Settings settings; private Settings settings;
private TreeView itemTreeView; private TreeView itemTreeView;
private Label statusLabel; private Label statusLabel;
private Builder builder;
// TODO: whenever we get something from the builder, cache it for later
// that way we don't need to constantly do "builder.GetObject"s
static SemaphoreSlim tasklimit = new SemaphoreSlim(4); static SemaphoreSlim tasklimit = new SemaphoreSlim(4);
private Listing SelectedItem {
get {
this.itemTreeView.Selection.GetSelected(out TreeIter iter);
return (Listing)this.itemTreeView.Model.GetValue(iter, 0);
}
}
public MainWindow() : this(new Builder("main.glade")) { } public MainWindow() : this(new Builder("main.glade")) { }
private MainWindow(Builder builder) : base(builder.GetObject("wndMain").Handle) { private MainWindow(Builder builder) : base(builder.GetObject("wndMain").Handle) {
@ -78,6 +90,7 @@ namespace Buypeeb {
this.SaveSettings(); this.SaveSettings();
this.Title = "Buypeeb"; this.Title = "Buypeeb";
this.builder = builder;
builder.Autoconnect(this); builder.Autoconnect(this);
this.statusLabel = (Label)builder.GetObject("LabelStatus"); this.statusLabel = (Label)builder.GetObject("LabelStatus");
@ -164,13 +177,18 @@ namespace Buypeeb {
using (WebClient client = new WebClient()) { using (WebClient client = new WebClient()) {
// TODO: download should have timeout // TODO: download should have timeout
item.Update(client.DownloadString(item.url)); // item.Update(client.DownloadString(item.url));
// item.Update(File.ReadAllText("yahoo.html")); item.Update(File.ReadAllText("yahoo.html"));
} }
Gtk.Application.Invoke(delegate { Gtk.Application.Invoke(delegate {
var pathAndIter = this.GetRow(id); var pathAndIter = this.GetRow(id);
this.items.EmitRowChanged(pathAndIter.path, pathAndIter.iter); this.items.EmitRowChanged(pathAndIter.path, pathAndIter.iter);
if (item == this.SelectedItem) {
// if the user has this item selected and it just became ready, enable the selection box
var s = (Box)this.builder.GetObject("SelectionViewBox");
s.Sensitive = true;
}
}); });
item.ready = true; item.ready = true;
@ -188,20 +206,78 @@ namespace Buypeeb {
} }
private void UpdateItems() { private void UpdateItems() {
var s = (Box)this.builder.GetObject("SelectionViewBox");
s.Sensitive = false;
var t = Task.Factory.StartNew(() => { var t = Task.Factory.StartNew(() => {
foreach (var item in this.settings.watchlist) { foreach (var item in this.settings.watchlist) {
this.UpdateItem(item.Key); this.UpdateItem(item.Key);
} }
}); });
}
private void UpdateSelectionView() {
// get the currently selected item
var item = this.SelectedItem;
var s = (Box)this.builder.GetObject("SelectionViewBox");
var infobox = (Box)this.builder.GetObject("SelectionInfoBox");
if (item == null) {
s.Sensitive = false;
infobox.Visible = false;
var l = (Label)this.builder.GetObject("LabelSelectedName");
l.Text = "buypeeb";
return;
}
s.Sensitive = item.ready;
infobox.Visible = true;
var info = new Dictionary<string, string>();
info.Add("Name", item.name);
info.Add("YahooName", item.original_name);
info.Add("Price", item.PriceJPY());
info.Add("PriceAUD", item.PriceAUD());
info.Add("Ending", "whenever");
info.Add("Bids", $"{item.bids}");
info.Add("BuyItNow", item.win_price == 0 ? "No" : $"¥{item.PriceJPY(true)} (${item.PriceAUD(true)})");
info.Add("AutoExtension", item.auto_extension ? "Yes" : "No");
info.Add("LastUpdated", "Last updated: heeeenlo");
foreach (var row in info) {
var l = (Label)this.builder.GetObject($"LabelSelected{row.Key}");
l.Text = row.Value;
}
}
private void OpenUrl(string url) {
// https://github.com/dotnet/runtime/issues/17938
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
ProcessStartInfo psi = new ProcessStartInfo {
FileName = url,
UseShellExecute = true
};
Process.Start(psi);
}
else {
// let's hope you have xdg-open installed
Process.Start("xdg-open", url);
}
} }
// show a simple entry dialogue that allows the user to enter text and either cancel or submit it // show a simple entry dialogue that allows the user to enter text and either cancel or submit it
private (Boolean accepted, string response) EntryDialogue(string title = "Buypeeb", string message = "Hi there!") { private (Boolean accepted, string response) EntryDialogue(string title = "Buypeeb", string message = "Hi there!", string prefill = null) {
Dialog ed = new Dialog(title, null, Gtk.DialogFlags.DestroyWithParent, "Cancel", ResponseType.Cancel, "OK", ResponseType.Ok); Dialog ed = new Dialog(title, null, DialogFlags.DestroyWithParent | DialogFlags.Modal, "Cancel", ResponseType.Cancel, "OK", ResponseType.Ok);
ed.DefaultResponse = ResponseType.Ok; ed.DefaultResponse = ResponseType.Ok;
ed.KeepAbove = true;
Label edLabel = new Label(message); Label edLabel = new Label(message);
Entry edEntry = new Entry(); Entry edEntry = new Entry();
if (!String.IsNullOrWhiteSpace(prefill)) {
edEntry.Text = prefill;
}
edEntry.ActivatesDefault = true; edEntry.ActivatesDefault = true;
ed.ContentArea.PackStart(edLabel, true, true, 2); ed.ContentArea.PackStart(edLabel, true, true, 2);
@ -209,6 +285,12 @@ namespace Buypeeb {
ed.ContentArea.PackStart(edEntry, true, true, 10); ed.ContentArea.PackStart(edEntry, true, true, 10);
edEntry.Show(); edEntry.Show();
ed.ContentArea.MarginBottom = 5;
ed.ContentArea.MarginTop = 5;
ed.ContentArea.MarginStart = 5;
ed.ContentArea.MarginEnd = 5;
ed.ActionArea.MarginBottom = 5; // TODO: apparently actionarea is obsolete
ResponseType accepted = (ResponseType)ed.Run(); ResponseType accepted = (ResponseType)ed.Run();
string response = edEntry.Text; string response = edEntry.Text;
ed.Dispose(); ed.Dispose();
@ -222,11 +304,12 @@ namespace Buypeeb {
} }
} }
// button handlers // event handlers
private void ButtonAddClicked(object sender, EventArgs a) { private void ButtonAddClicked(object sender, EventArgs a) {
// Console.WriteLine("ButtonAddClicked"); // Console.WriteLine("ButtonAddClicked");
AddItemDialogue aid = new AddItemDialogue(); AddItemDialogue aid = new AddItemDialogue();
aid.Title = "Buypeeb";
ResponseType accepted = (ResponseType)aid.Run(); ResponseType accepted = (ResponseType)aid.Run();
string url = aid.GetURL(); string url = aid.GetURL();
string name = aid.GetName(); string name = aid.GetName();
@ -236,7 +319,7 @@ namespace Buypeeb {
Regex rx = new Regex(@"^http.+yahoo.+"); Regex rx = new Regex(@"^http.+yahoo.+");
if (rx.IsMatch(url)) { if (rx.IsMatch(url)) {
Console.WriteLine("{0} will be added", url); Console.WriteLine("{0} will be added", url);
this.settings.Watch(url, name); this.UpdateItem(this.settings.Watch(url, name).id);
this.RenderList(); this.RenderList();
} }
else { else {
@ -261,7 +344,15 @@ namespace Buypeeb {
} }
private void ButtonQuitClicked(object sender, EventArgs a) { private void ButtonQuitClicked(object sender, EventArgs a) {
MessageDialog md = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel, "Are you sure you want to quit?"); MessageDialog md = new MessageDialog(
parent_window: this,
flags: DialogFlags.DestroyWithParent | DialogFlags.Modal,
type: MessageType.Question,
bt: ButtonsType.OkCancel,
format: "Are you sure you want to quit?"
);
md.KeepAbove = true;
ResponseType response = (ResponseType)md.Run(); ResponseType response = (ResponseType)md.Run();
md.Dispose(); md.Dispose();
if (response == ResponseType.Ok) { if (response == ResponseType.Ok) {
@ -270,24 +361,51 @@ namespace Buypeeb {
} }
} }
private void tveItemsSelectionChanged(object sender, EventArgs a) { private void TreeViewItemsSelectionChanged(object sender, EventArgs a) {
Console.WriteLine("tveItemsSelectionChanged"); this.UpdateSelectionView();
} }
private void ButtonViewBuyeeClicked(object sender, EventArgs a) { private void ButtonViewBuyeeClicked(object sender, EventArgs a) {
Console.WriteLine("ButtonViewBuyeeClicked"); this.OpenUrl(this.SelectedItem.buyee_url);
} }
private void ButtonViewYahooClicked(object sender, EventArgs a) { private void ButtonViewYahooClicked(object sender, EventArgs a) {
Console.WriteLine("ButtonViewYahooClicked"); this.OpenUrl(this.SelectedItem.url);
} }
private void ButtonSelectedRemoveClicked(object sender, EventArgs a) { private void ButtonSelectedRemoveClicked(object sender, EventArgs a) {
Console.WriteLine("ButtonSelectedRemoveClicked"); var item = this.SelectedItem;
MessageDialog md = new MessageDialog(
parent_window: this,
flags: DialogFlags.DestroyWithParent | DialogFlags.Modal,
type: MessageType.Question,
bt: ButtonsType.OkCancel,
format: $"Are you sure you want to remove the item \"{item.name}\"?" // TODO: this looks bad being all on one line
);
md.KeepAbove = true;
ResponseType response = (ResponseType)md.Run();
md.Dispose();
if (response == ResponseType.Ok) {
this.settings.watchlist.Remove(item.id);
this.RenderList();
}
} }
private void ButtonSelectedRenameClicked(object sender, EventArgs a) { private void ButtonSelectedRenameClicked(object sender, EventArgs a) {
Console.WriteLine("ButtonSelectedRenameClicked"); var item = this.SelectedItem;
(bool accepted, string response) = this.EntryDialogue("Rename item", $"Enter a new name for the item \"{item.name}\".", item.name);
if (accepted) {
item.name = response;
this.UpdateSelectionView();
}
}
private void ButtonSelectedUpdateClicked(object sender, EventArgs args) {
var s = (Box)this.builder.GetObject("SelectionViewBox");
s.Sensitive = false;
this.UpdateItem(this.SelectedItem.id);
} }
// column renderers // column renderers

View file

@ -28,14 +28,11 @@ namespace Buypeeb {
} }
public void Watch(string url, string name) { public Listing Watch(string url, string name) {
string id = BuypeebApp.IDFromURL(url); string id = BuypeebApp.IDFromURL(url);
Console.WriteLine(id); Console.WriteLine(id);
this.watchlist[id] = new Listing(id, name); this.watchlist[id] = new Listing(id, name);
return this.watchlist[id];
foreach (KeyValuePair<string, Listing> entry in this.watchlist) {
Console.WriteLine("{0} - {1}", entry.Value.name, entry.Value.price);
}
} }
} }
} }

View file

@ -275,7 +275,7 @@
<property name="activate_on_single_click">True</property> <property name="activate_on_single_click">True</property>
<child internal-child="selection"> <child internal-child="selection">
<object class="GtkTreeSelection"> <object class="GtkTreeSelection">
<signal name="changed" handler="tveItemsSelectionChanged" swapped="no"/> <signal name="changed" handler="TreeViewItemsSelectionChanged" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
@ -335,14 +335,15 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox" id="SelectionViewBox">
<property name="width_request">200</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_start">5</property> <property name="margin_start">5</property>
<property name="margin_end">5</property> <property name="margin_end">5</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkLabel" id="lblSelectedName"> <object class="GtkLabel" id="LabelSelectedName">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">precious peebus polytonal player</property> <property name="label" translatable="yes">precious peebus polytonal player</property>
@ -375,6 +376,11 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkBox" id="SelectionInfoBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="visible">True</property> <property name="visible">True</property>
@ -442,7 +448,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedYahooName"> <object class="GtkLabel" id="LabelSelectedYahooName">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -455,7 +461,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedPrice"> <object class="GtkLabel" id="LabelSelectedPrice">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -467,7 +473,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedPriceAUD"> <object class="GtkLabel" id="LabelSelectedPriceAUD">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -479,7 +485,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedEnding"> <object class="GtkLabel" id="LabelSelectedEnding">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -491,7 +497,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedBids"> <object class="GtkLabel" id="LabelSelectedBids">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -540,7 +546,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedBuyItNow"> <object class="GtkLabel" id="LabelSelectedBuyItNow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -552,7 +558,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="lblSelectedAutoExtension"> <object class="GtkLabel" id="LabelSelectedAutoExtension">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">end</property> <property name="halign">end</property>
@ -582,24 +588,10 @@
<placeholder/> <placeholder/>
</child> </child>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lblSelectedLastUpdated">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Last updated: 12:34:56</property>
<property name="justify">center</property>
<property name="wrap">True</property>
</object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">3</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -692,6 +684,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Update</property> <property name="tooltip_text" translatable="yes">Update</property>
<signal name="clicked" handler="ButtonSelectedUpdateClicked" swapped="no"/>
<child> <child>
<object class="GtkImage" id="image2"> <object class="GtkImage" id="image2">
<property name="visible">True</property> <property name="visible">True</property>
@ -737,7 +730,30 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">4</property> <property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="LabelSelectedLastUpdated">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Last updated: 12:34:56</property>
<property name="justify">center</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>