Compare commits
3 Commits
main
...
7507030f72
| Author | SHA1 | Date | |
|---|---|---|---|
| 7507030f72 | |||
| e9c5464df0 | |||
| e3827d97e8 |
@@ -11,6 +11,18 @@ public class SavedListing
|
||||
public string ConditionNotes { get; set; } = "";
|
||||
public string ExportFolder { get; set; } = "";
|
||||
|
||||
/// <summary>eBay category ID — stored at save time so we can post without re-looking it up.</summary>
|
||||
public string CategoryId { get; set; } = "";
|
||||
|
||||
/// <summary>Item condition — defaults to Used for existing records without this field.</summary>
|
||||
public ItemCondition Condition { get; set; } = ItemCondition.Used;
|
||||
|
||||
/// <summary>Listing format — defaults to FixedPrice for existing records.</summary>
|
||||
public ListingFormat Format { get; set; } = ListingFormat.FixedPrice;
|
||||
|
||||
/// <summary>Seller postcode — populated from appsettings default at save time.</summary>
|
||||
public string Postcode { get; set; } = "";
|
||||
|
||||
/// <summary>Absolute paths to photos inside ExportFolder.</summary>
|
||||
public List<string> PhotoPaths { get; set; } = new();
|
||||
|
||||
@@ -19,4 +31,21 @@ public class SavedListing
|
||||
public string PriceDisplay => Price > 0 ? $"£{Price:F2}" : "—";
|
||||
|
||||
public string SavedAtDisplay => SavedAt.ToLocalTime().ToString("d MMM yyyy, HH:mm");
|
||||
|
||||
/// <summary>
|
||||
/// Converts this saved draft back into a ListingDraft suitable for PostListingAsync.
|
||||
/// </summary>
|
||||
public ListingDraft ToListingDraft() => new ListingDraft
|
||||
{
|
||||
Title = Title,
|
||||
Description = Description,
|
||||
Price = Price,
|
||||
CategoryId = CategoryId,
|
||||
CategoryName = Category,
|
||||
Condition = Condition,
|
||||
Format = Format,
|
||||
Postcode = Postcode,
|
||||
PhotoPaths = new List<string>(PhotoPaths),
|
||||
Quantity = 1
|
||||
};
|
||||
}
|
||||
|
||||
115
EbayListingTool/Views/NewListingView.xaml
Normal file
115
EbayListingTool/Views/NewListingView.xaml
Normal file
@@ -0,0 +1,115 @@
|
||||
<UserControl x:Class="EbayListingTool.Views.NewListingView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
|
||||
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||
Loaded="UserControl_Loaded">
|
||||
|
||||
<!-- Root grid hosts all three states; Visibility toggled in code-behind -->
|
||||
<Grid>
|
||||
<!-- ══════════════════════════════════════ STATE A: Drop Zone -->
|
||||
<Grid x:Name="StateA" Visibility="Visible">
|
||||
<DockPanel LastChildFill="True">
|
||||
|
||||
<!-- Loading panel — shown while AI runs -->
|
||||
<Border x:Name="LoadingPanel" DockPanel.Dock="Top"
|
||||
Visibility="Collapsed"
|
||||
Margin="60,30,60,0" Padding="30,40"
|
||||
Background="{DynamicResource MahApps.Brushes.Gray9}"
|
||||
CornerRadius="10">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<mah:ProgressRing Width="36" Height="36"
|
||||
HorizontalAlignment="Center" Margin="0,0,0,16"/>
|
||||
<TextBlock x:Name="LoadingStepText"
|
||||
Text="Examining the photo…"
|
||||
FontSize="14" FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray1}"/>
|
||||
<TextBlock Text="This usually takes 10–20 seconds"
|
||||
FontSize="11" HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray5}"
|
||||
Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Drop zone -->
|
||||
<Border x:Name="DropZoneBorder" DockPanel.Dock="Top"
|
||||
Margin="60,30,60,0"
|
||||
AllowDrop="True"
|
||||
MouseLeftButtonUp="DropZone_Click"
|
||||
DragOver="DropZone_DragOver"
|
||||
DragEnter="DropZone_DragEnter"
|
||||
DragLeave="DropZone_DragLeave"
|
||||
Drop="DropZone_Drop"
|
||||
Cursor="Hand"
|
||||
MinHeight="180">
|
||||
<Grid>
|
||||
<!-- Dashed border via Rectangle -->
|
||||
<Rectangle x:Name="DropBorderRect"
|
||||
StrokeThickness="2"
|
||||
StrokeDashArray="6,4"
|
||||
RadiusX="10" RadiusY="10"
|
||||
Stroke="{DynamicResource MahApps.Brushes.Gray6}"/>
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Margin="0,40">
|
||||
<iconPacks:PackIconMaterial Kind="CameraOutline"
|
||||
Width="52" Height="52"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray5}"
|
||||
Margin="0,0,0,16"/>
|
||||
<TextBlock Text="Drop photos here"
|
||||
FontSize="18" FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray2}"/>
|
||||
<TextBlock Text="or click to browse — up to 12 photos"
|
||||
FontSize="12" HorizontalAlignment="Center"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray5}"
|
||||
Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Thumbnail strip -->
|
||||
<ScrollViewer x:Name="ThumbScroller" DockPanel.Dock="Top"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
Margin="60,12,60,0" Visibility="Collapsed">
|
||||
<StackPanel x:Name="ThumbStrip" Orientation="Horizontal"/>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Analyse button -->
|
||||
<StackPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="0,20,0,0">
|
||||
<Button x:Name="AnalyseBtn"
|
||||
Click="Analyse_Click"
|
||||
IsEnabled="False"
|
||||
Style="{StaticResource MahApps.Styles.Button.Square.Accent}"
|
||||
Padding="28,12" FontSize="14" FontWeight="SemiBold">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<iconPacks:PackIconMaterial x:Name="AnalyseIcon"
|
||||
Kind="MagnifyScan" Width="18" Height="18"
|
||||
Margin="0,0,8,0" VerticalAlignment="Center"/>
|
||||
<mah:ProgressRing x:Name="AnalyseSpinner"
|
||||
Width="18" Height="18" Margin="0,0,8,0"
|
||||
Visibility="Collapsed"/>
|
||||
<TextBlock x:Name="AnalyseBtnText"
|
||||
Text="Identify & Price with AI"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<TextBlock x:Name="PhotoCountLabel"
|
||||
HorizontalAlignment="Center" Margin="0,8,0,0"
|
||||
FontSize="11" Visibility="Collapsed"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray5}"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid/> <!-- fill remaining space -->
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- ══════════════════════════════════════ STATE B: Review & Edit (stub for now) -->
|
||||
<Grid x:Name="StateB" Visibility="Collapsed"/>
|
||||
|
||||
<!-- ══════════════════════════════════════ STATE C: Success (stub for now) -->
|
||||
<Grid x:Name="StateC" Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
235
EbayListingTool/Views/NewListingView.xaml.cs
Normal file
235
EbayListingTool/Views/NewListingView.xaml.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using EbayListingTool.Models;
|
||||
using EbayListingTool.Services;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace EbayListingTool.Views;
|
||||
|
||||
public partial class NewListingView : UserControl
|
||||
{
|
||||
// Services (injected via Initialise)
|
||||
private EbayListingService? _listingService;
|
||||
private EbayCategoryService? _categoryService;
|
||||
private AiAssistantService? _aiService;
|
||||
private EbayAuthService? _auth;
|
||||
private SavedListingsService? _savedService;
|
||||
private string _defaultPostcode = "";
|
||||
|
||||
// State A — photos
|
||||
private readonly List<string> _photoPaths = new();
|
||||
private const int MaxPhotos = 12;
|
||||
|
||||
// State B — draft being edited (stub, populated in Task 4)
|
||||
private ListingDraft _draft = new();
|
||||
private PhotoAnalysisResult? _lastAnalysis;
|
||||
private bool _suppressCategoryLookup;
|
||||
private System.Threading.CancellationTokenSource? _categoryCts;
|
||||
private string _suggestedPriceValue = "";
|
||||
|
||||
// Loading step cycling
|
||||
private readonly DispatcherTimer _loadingTimer;
|
||||
private int _loadingStep;
|
||||
private static readonly string[] LoadingSteps =
|
||||
[
|
||||
"Examining the photo\u2026",
|
||||
"Identifying the item\u2026",
|
||||
"Researching eBay prices\u2026",
|
||||
"Writing description\u2026"
|
||||
];
|
||||
|
||||
public NewListingView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_loadingTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2.5) };
|
||||
_loadingTimer.Tick += (_, _) =>
|
||||
{
|
||||
_loadingStep = (_loadingStep + 1) % LoadingSteps.Length;
|
||||
LoadingStepText.Text = LoadingSteps[_loadingStep];
|
||||
};
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e) { }
|
||||
|
||||
public void Initialise(EbayListingService listingService, EbayCategoryService categoryService,
|
||||
AiAssistantService aiService, EbayAuthService auth,
|
||||
SavedListingsService savedService, string defaultPostcode)
|
||||
{
|
||||
_listingService = listingService;
|
||||
_categoryService = categoryService;
|
||||
_aiService = aiService;
|
||||
_auth = auth;
|
||||
_savedService = savedService;
|
||||
_defaultPostcode = defaultPostcode;
|
||||
}
|
||||
|
||||
// ---- State machine ----
|
||||
|
||||
private enum ListingState { DropZone, ReviewEdit, Success }
|
||||
|
||||
private void SetState(ListingState state)
|
||||
{
|
||||
StateA.Visibility = state == ListingState.DropZone ? Visibility.Visible : Visibility.Collapsed;
|
||||
StateB.Visibility = state == ListingState.ReviewEdit ? Visibility.Visible : Visibility.Collapsed;
|
||||
StateC.Visibility = state == ListingState.Success ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// ---- State A: Drop zone ----
|
||||
|
||||
private void DropZone_DragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop)
|
||||
? DragDropEffects.Copy : DragDropEffects.None;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void DropZone_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
DropBorderRect.Stroke = (System.Windows.Media.Brush)FindResource("MahApps.Brushes.Accent");
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void DropZone_DragLeave(object sender, DragEventArgs e)
|
||||
{
|
||||
DropBorderRect.Stroke = (System.Windows.Media.Brush)FindResource("MahApps.Brushes.Gray6");
|
||||
}
|
||||
|
||||
private void DropZone_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
DropZone_DragLeave(sender, e);
|
||||
if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
|
||||
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
AddPhotos(files.Where(IsImageFile).ToArray());
|
||||
}
|
||||
|
||||
private void DropZone_Click(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var dlg = new OpenFileDialog
|
||||
{
|
||||
Title = "Select photos of the item",
|
||||
Filter = "Images|*.jpg;*.jpeg;*.png;*.gif;*.webp;*.bmp|All files|*.*",
|
||||
Multiselect = true
|
||||
};
|
||||
if (dlg.ShowDialog() == true)
|
||||
AddPhotos(dlg.FileNames);
|
||||
}
|
||||
|
||||
private void AddPhotos(string[] paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (_photoPaths.Count >= MaxPhotos) break;
|
||||
if (_photoPaths.Contains(path)) continue;
|
||||
if (!IsImageFile(path)) continue;
|
||||
_photoPaths.Add(path);
|
||||
}
|
||||
UpdateThumbStrip();
|
||||
UpdateAnalyseButton();
|
||||
}
|
||||
|
||||
private void UpdateThumbStrip()
|
||||
{
|
||||
ThumbStrip.Children.Clear();
|
||||
ThumbScroller.Visibility = _photoPaths.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
foreach (var path in _photoPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bmp = new BitmapImage();
|
||||
bmp.BeginInit();
|
||||
bmp.UriSource = new Uri(path, UriKind.Absolute);
|
||||
bmp.DecodePixelWidth = 80;
|
||||
bmp.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bmp.EndInit();
|
||||
bmp.Freeze();
|
||||
|
||||
var img = new Image
|
||||
{
|
||||
Source = bmp, Width = 60, Height = 60,
|
||||
Stretch = System.Windows.Media.Stretch.UniformToFill,
|
||||
Margin = new Thickness(3)
|
||||
};
|
||||
img.Clip = new System.Windows.Media.RectangleGeometry(
|
||||
new Rect(0, 0, 60, 60), 4, 4);
|
||||
ThumbStrip.Children.Add(img);
|
||||
}
|
||||
catch { /* skip bad files */ }
|
||||
}
|
||||
|
||||
PhotoCountLabel.Text = $"{_photoPaths.Count} / {MaxPhotos} photos";
|
||||
PhotoCountLabel.Visibility = _photoPaths.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void UpdateAnalyseButton()
|
||||
{
|
||||
AnalyseBtn.IsEnabled = _photoPaths.Count > 0;
|
||||
}
|
||||
|
||||
private async void Analyse_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_aiService == null || _photoPaths.Count == 0) return;
|
||||
SetAnalysing(true);
|
||||
try
|
||||
{
|
||||
var result = await _aiService.AnalyseItemFromPhotosAsync(_photoPaths);
|
||||
_lastAnalysis = result;
|
||||
await PopulateStateBAsync(result);
|
||||
SetState(ListingState.ReviewEdit);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Analysis failed:\n\n{ex.Message}", "AI Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetAnalysing(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAnalysing(bool busy)
|
||||
{
|
||||
AnalyseBtn.IsEnabled = !busy;
|
||||
AnalyseSpinner.Visibility = busy ? Visibility.Visible : Visibility.Collapsed;
|
||||
AnalyseIcon.Visibility = busy ? Visibility.Collapsed : Visibility.Visible;
|
||||
AnalyseBtnText.Text = busy ? "Analysing\u2026" : "Identify & Price with AI";
|
||||
LoadingPanel.Visibility = busy ? Visibility.Visible : Visibility.Collapsed;
|
||||
DropZoneBorder.Visibility = busy ? Visibility.Collapsed : Visibility.Visible;
|
||||
if (busy)
|
||||
{
|
||||
_loadingStep = 0;
|
||||
LoadingStepText.Text = LoadingSteps[0];
|
||||
_loadingTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
_loadingTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Stub for State B — implemented in Task 4
|
||||
private Task PopulateStateBAsync(PhotoAnalysisResult result) => Task.CompletedTask;
|
||||
|
||||
// Stub for ResetToStateA — implemented in Task 4
|
||||
public void ResetToStateA()
|
||||
{
|
||||
_photoPaths.Clear();
|
||||
_draft = new ListingDraft { Postcode = _defaultPostcode };
|
||||
_lastAnalysis = null;
|
||||
UpdateThumbStrip();
|
||||
UpdateAnalyseButton();
|
||||
SetState(ListingState.DropZone);
|
||||
}
|
||||
|
||||
private static bool IsImageFile(string path)
|
||||
{
|
||||
var ext = System.IO.Path.GetExtension(path).ToLowerInvariant();
|
||||
return ext is ".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" or ".bmp";
|
||||
}
|
||||
|
||||
private MainWindow? GetWindow() => Window.GetWindow(this) as MainWindow;
|
||||
}
|
||||
@@ -318,6 +318,21 @@
|
||||
|
||||
<!-- Action buttons -->
|
||||
<WrapPanel Orientation="Horizontal">
|
||||
<Button x:Name="PostDraftBtn"
|
||||
Click="PostDraft_Click"
|
||||
Style="{StaticResource MahApps.Styles.Button.Square.Accent}"
|
||||
Height="34" Padding="14,0" Margin="0,0,8,6"
|
||||
ToolTip="Post this draft to eBay">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<iconPacks:PackIconMaterial x:Name="PostDraftIcon"
|
||||
Kind="CartArrowRight" Width="14" Height="14"
|
||||
Margin="0,0,6,0" VerticalAlignment="Center"/>
|
||||
<mah:ProgressRing x:Name="PostDraftSpinner"
|
||||
Width="14" Height="14" Margin="0,0,6,0"
|
||||
Visibility="Collapsed"/>
|
||||
<TextBlock Text="Post to eBay" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Click="EditListing_Click"
|
||||
Style="{DynamicResource MahApps.Styles.Button.Square.Accent}"
|
||||
Height="34" Padding="14,0" Margin="0,0,8,6">
|
||||
@@ -443,5 +458,40 @@
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Draft posted toast -->
|
||||
<Border x:Name="DraftPostedToast"
|
||||
Grid.Column="2"
|
||||
Visibility="Collapsed"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="12" Padding="14,10"
|
||||
CornerRadius="6"
|
||||
Background="{DynamicResource MahApps.Brushes.Gray8}"
|
||||
BorderThickness="0,0,0,3"
|
||||
BorderBrush="{DynamicResource MahApps.Brushes.Accent}"
|
||||
Panel.ZIndex="10">
|
||||
<Border.RenderTransform>
|
||||
<TranslateTransform x:Name="ToastTranslate" Y="60"/>
|
||||
</Border.RenderTransform>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<iconPacks:PackIconMaterial Kind="CheckCircleOutline"
|
||||
Width="16" Height="16" Margin="0,0,10,0"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Accent}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="ToastUrlText" Grid.Column="1"
|
||||
VerticalAlignment="Center" TextTrimming="CharacterEllipsis"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray1}" FontSize="12"/>
|
||||
<Button Grid.Column="2" Content="✕" Width="20" Height="20"
|
||||
BorderThickness="0" Background="Transparent"
|
||||
Foreground="{DynamicResource MahApps.Brushes.Gray5}"
|
||||
Click="DismissToast_Click" Margin="8,0,0,0"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using EbayListingTool.Models;
|
||||
using EbayListingTool.Services;
|
||||
using Microsoft.Win32;
|
||||
@@ -14,6 +15,8 @@ public partial class SavedListingsView : UserControl
|
||||
{
|
||||
private SavedListingsService? _service;
|
||||
private PriceLookupService? _priceLookup;
|
||||
private EbayListingService? _ebayListing;
|
||||
private EbayAuthService? _ebayAuth;
|
||||
private SavedListing? _selected;
|
||||
|
||||
// Edit mode working state
|
||||
@@ -34,10 +37,15 @@ public partial class SavedListingsView : UserControl
|
||||
};
|
||||
}
|
||||
|
||||
public void Initialise(SavedListingsService service, PriceLookupService? priceLookup = null)
|
||||
public void Initialise(SavedListingsService service,
|
||||
PriceLookupService? priceLookup = null,
|
||||
EbayListingService? ebayListing = null,
|
||||
EbayAuthService? ebayAuth = null)
|
||||
{
|
||||
_service = service;
|
||||
_priceLookup = priceLookup;
|
||||
_service = service;
|
||||
_priceLookup = priceLookup;
|
||||
_ebayListing = ebayListing;
|
||||
_ebayAuth = ebayAuth;
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
@@ -753,4 +761,87 @@ public partial class SavedListingsView : UserControl
|
||||
ClearDetail();
|
||||
RefreshList();
|
||||
}
|
||||
|
||||
private async void PostDraft_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_selected == null || _ebayListing == null || _ebayAuth == null) return;
|
||||
if (!_ebayAuth.IsConnected)
|
||||
{
|
||||
MessageBox.Show("Please connect to eBay first.", "Not Connected",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
SetPostDraftBusy(true);
|
||||
try
|
||||
{
|
||||
var draft = _selected.ToListingDraft();
|
||||
var url = await _ebayListing.PostListingAsync(draft);
|
||||
|
||||
ToastUrlText.Text = url;
|
||||
ShowDraftPostedToast();
|
||||
|
||||
var posted = _selected;
|
||||
_selected = null;
|
||||
_service?.Delete(posted);
|
||||
ClearDetail();
|
||||
RefreshList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Failed to post listing:\n\n{ex.Message}", "Post Failed",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetPostDraftBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPostDraftBusy(bool busy)
|
||||
{
|
||||
PostDraftBtn.IsEnabled = !busy;
|
||||
PostDraftIcon.Visibility = busy ? Visibility.Collapsed : Visibility.Visible;
|
||||
PostDraftSpinner.Visibility = busy ? Visibility.Visible : Visibility.Collapsed;
|
||||
IsEnabled = !busy;
|
||||
}
|
||||
|
||||
private DispatcherTimer? _toastTimer;
|
||||
|
||||
private void ShowDraftPostedToast()
|
||||
{
|
||||
_toastTimer?.Stop();
|
||||
ToastTranslate.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, null);
|
||||
DraftPostedToast.Visibility = Visibility.Visible;
|
||||
|
||||
var slideIn = new System.Windows.Media.Animation.DoubleAnimation(
|
||||
60, 0, new Duration(TimeSpan.FromMilliseconds(220)))
|
||||
{
|
||||
EasingFunction = new System.Windows.Media.Animation.CubicEase
|
||||
{ EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut }
|
||||
};
|
||||
slideIn.Completed += (_, _) =>
|
||||
{
|
||||
_toastTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(8) };
|
||||
_toastTimer.Tick += (_, _) => DismissToastAnimated();
|
||||
_toastTimer.Start();
|
||||
};
|
||||
ToastTranslate.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, slideIn);
|
||||
}
|
||||
|
||||
private void DismissToast_Click(object sender, RoutedEventArgs e)
|
||||
=> DismissToastAnimated();
|
||||
|
||||
private void DismissToastAnimated()
|
||||
{
|
||||
_toastTimer?.Stop();
|
||||
var slideOut = new System.Windows.Media.Animation.DoubleAnimation(
|
||||
0, 60, new Duration(TimeSpan.FromMilliseconds(180)))
|
||||
{
|
||||
EasingFunction = new System.Windows.Media.Animation.CubicEase
|
||||
{ EasingMode = System.Windows.Media.Animation.EasingMode.EaseIn }
|
||||
};
|
||||
slideOut.Completed += (_, _) => DraftPostedToast.Visibility = Visibility.Collapsed;
|
||||
ToastTranslate.BeginAnimation(System.Windows.Media.TranslateTransform.YProperty, slideOut);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user