mirror of
https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies.git
synced 2026-07-25 06:14:31 +00:00
feat(confirmations): reveal items in grouped market sell confirmations (#18)
Grouped market sell confirmations previously appeared as a single row showing only the item count (e.g. "Sell 4 pcs"). This made it difficult to verify which items were being confirmed or their prices. Add an expandable grouped confirmation that reveals individual items with their icon, name and price. Also allow confirming or canceling items individually while keeping confirm-all and cancel-all actions. Includes a scrollable list for large batches and minor UI/code cleanup. Co-authored-by: bohdanbtw [bohdanbtw@DESKTOP-ANGF2KJ](mailto:bohdanbtw@DESKTOP-ANGF2KJ)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MaterialDesignThemes.Wpf;
|
using MaterialDesignThemes.Wpf;
|
||||||
using NebulaAuth.Model;
|
using NebulaAuth.Model;
|
||||||
@@ -130,7 +130,7 @@ public static class DialogsController
|
|||||||
var content = msg == null ? new ConfirmCancelDialog() : new ConfirmCancelDialog(msg);
|
var content = msg == null ? new ConfirmCancelDialog() : new ConfirmCancelDialog(msg);
|
||||||
|
|
||||||
var result = await DialogHost.Show(content);
|
var result = await DialogHost.Show(content);
|
||||||
return result != null && (bool) result;
|
return result != null && (bool)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string?> ShowTextFieldDialog(string? title = null, string? msg = null)
|
public static async Task<string?> ShowTextFieldDialog(string? title = null, string? msg = null)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -51,7 +51,7 @@ public static class Storage
|
|||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
return Parallel.ForEachAsync(files,
|
return Parallel.ForEachAsync(files,
|
||||||
new ParallelOptions {CancellationToken = token, MaxDegreeOfParallelism = threadCount},
|
new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = threadCount },
|
||||||
async (file, ct) =>
|
async (file, ct) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -218,7 +218,7 @@ public static class Storage
|
|||||||
|
|
||||||
if (counter % 5 == 0)
|
if (counter % 5 == 0)
|
||||||
{
|
{
|
||||||
progress?.Report(counter / (double) files.Count);
|
progress?.Report(counter / (double)files.Count);
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:confirmations="clr-namespace:SteamLib.SteamMobile.Confirmations;assembly=SteamLibForked"
|
xmlns:confirmations="clr-namespace:SteamLib.SteamMobile.Confirmations;assembly=SteamLibForked"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
|
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
|
||||||
xmlns:vm="clr-namespace:NebulaAuth.ViewModel">
|
xmlns:vm="clr-namespace:NebulaAuth.ViewModel">
|
||||||
|
|
||||||
|
|
||||||
<DataTemplate DataType="{x:Type confirmations:Confirmation}">
|
<DataTemplate DataType="{x:Type confirmations:Confirmation}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -175,36 +174,96 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
<DataTemplate DataType="{x:Type entities:MarketMultiConfirmation}">
|
<DataTemplate DataType="{x:Type entities:MarketMultiConfirmation}">
|
||||||
<Grid>
|
<Expander Padding="0" Background="Transparent" materialDesign:ExpanderAssist.ExpanderButtonPosition="Start" materialDesign:ExpanderAssist.HorizontalHeaderPadding="0" HorizontalAlignment="Stretch">
|
||||||
<Grid.ColumnDefinitions>
|
<Expander.Header>
|
||||||
<ColumnDefinition Width="Auto" />
|
<Grid HorizontalAlignment="Stretch">
|
||||||
<ColumnDefinition Width="*" />
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
<ColumnDefinition Width="Auto" />
|
||||||
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="CartArrowUp"
|
<ColumnDefinition Width="Auto" />
|
||||||
Margin="0,0,10,0" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<TextBlock VerticalAlignment="Center" Grid.Column="1">
|
</Grid.ColumnDefinitions>
|
||||||
<Run Text="{Tr MainWindow.ConfirmationTemplates.Market, IsDynamic=False}" />
|
<materialDesign:PackIcon VerticalAlignment="Center" Width="20" Height="20" Kind="CartArrowUp"
|
||||||
<Run Text="{Binding Confirmations.Count, Mode=OneWay}" />
|
Margin="0,0,10,0" />
|
||||||
<Run Text="{Tr Common.Abbreviations.Count.Items, IsDynamic=False}" />
|
<TextBlock VerticalAlignment="Center" Grid.Column="1">
|
||||||
</TextBlock>
|
<Run Text="{Tr MainWindow.ConfirmationTemplates.Market, IsDynamic=False}" />
|
||||||
|
<Run Text="{Binding Confirmations.Count, Mode=OneWay}" />
|
||||||
|
<Run Text="{Tr Common.Abbreviations.Count.Items, IsDynamic=False}" />
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Left" Margin="5,0,0,0"
|
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Center" Margin="5,0,0,0"
|
||||||
Text="{Binding Time, StringFormat=t}" />
|
Text="{Binding Time, StringFormat=t}" />
|
||||||
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
|
||||||
Style="{StaticResource MaterialDesignIconButton}"
|
<!-- Confirm all -->
|
||||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="4"
|
||||||
CommandParameter="{Binding }">
|
Style="{StaticResource MaterialDesignIconButton}"
|
||||||
<materialDesign:PackIcon Kind="Check" />
|
ToolTip="{Tr MainWindow.ConfirmationTemplates.ConfirmAll}"
|
||||||
</Button>
|
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||||
<Button Grid.Column="4" Style="{StaticResource MaterialDesignIconButton}"
|
CommandParameter="{Binding }">
|
||||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.CancelCommand)}"
|
<materialDesign:PackIcon Kind="CheckAll" />
|
||||||
CommandParameter="{Binding }">
|
</Button>
|
||||||
<materialDesign:PackIcon Kind="Close" />
|
<!-- Cancel all -->
|
||||||
</Button>
|
<Button Grid.Column="5" Style="{StaticResource MaterialDesignIconButton}"
|
||||||
</Grid>
|
ToolTip="{Tr MainWindow.ConfirmationTemplates.CancelAll}"
|
||||||
|
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.CancelCommand)}"
|
||||||
|
CommandParameter="{Binding }">
|
||||||
|
<materialDesign:PackIcon Kind="Close" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Expander.Header>
|
||||||
|
|
||||||
|
<Border Margin="30,6,0,0" Background="{DynamicResource MaterialDesignPaper}" CornerRadius="4">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||||
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
|
MaxHeight="320">
|
||||||
|
<ItemsControl ItemsSource="{Binding Confirmations}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type confirmations:MarketConfirmation}">
|
||||||
|
<Grid Margin="0,4">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Image HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="32"
|
||||||
|
MaxHeight="32" Margin="0,0,10,0"
|
||||||
|
Source="{Binding ItemImageUri}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
|
<TextBlock TextWrapping="WrapWithOverflow" Text="{Binding ItemName}" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock VerticalAlignment="Center" Grid.Column="2" HorizontalAlignment="Left"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Text="{Binding Time, StringFormat=t}" />
|
||||||
|
|
||||||
|
<Button Margin="5,0,0,0" VerticalAlignment="Center" Grid.Column="3"
|
||||||
|
Style="{StaticResource MaterialDesignIconButton}"
|
||||||
|
ToolTip="{Tr MainWindow.ConfirmationTemplates.ConfirmOne}"
|
||||||
|
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||||
|
CommandParameter="{Binding}">
|
||||||
|
|
||||||
|
<materialDesign:PackIcon Kind="Check" />
|
||||||
|
</Button>
|
||||||
|
<Button Grid.Column="4" Style="{StaticResource MaterialDesignIconButton}"
|
||||||
|
ToolTip="{Tr MainWindow.ConfirmationTemplates.CancelOne}"
|
||||||
|
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.CancelCommand)}"
|
||||||
|
CommandParameter="{Binding}">
|
||||||
|
<materialDesign:PackIcon Kind="Close" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Expander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
<DataTemplate DataType="{x:Type confirmations:PurchaseConfirmation}">
|
<DataTemplate DataType="{x:Type confirmations:PurchaseConfirmation}">
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -227,7 +286,7 @@
|
|||||||
<materialDesign:PackIcon Kind="Check" />
|
<materialDesign:PackIcon Kind="Check" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button Grid.Column="4" Style="{StaticResource MaterialDesignIconButton}"
|
<Button Grid.Column="4" Style="{StaticResource MaterialDesignIconButton}"
|
||||||
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.CancelCommand)}"
|
Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.(vm:MainVM.ConfirmCommand)}"
|
||||||
CommandParameter="{Binding }">
|
CommandParameter="{Binding }">
|
||||||
<materialDesign:PackIcon Kind="Close" />
|
<materialDesign:PackIcon Kind="Close" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -76,18 +76,27 @@ public partial class MainVM //Confirmations
|
|||||||
return SendConfirmation(SelectedMafile, confirmation, false);
|
return SendConfirmation(SelectedMafile, confirmation, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async Task SendConfirmation(Mafile mafile, Confirmation confirmation, bool confirm)
|
private async Task SendConfirmation(Mafile mafile, Confirmation confirmation, bool confirm)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (confirmation is MarketMultiConfirmation multi)
|
switch (confirmation)
|
||||||
{
|
{
|
||||||
result = await MaClient.SendMultipleConfirmation(mafile, multi.Confirmations, confirm);
|
case MarketMultiConfirmation multi:
|
||||||
}
|
result = await MaClient.SendMultipleConfirmation(mafile, multi.Confirmations, confirm);
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
result = await MaClient.SendConfirmation(mafile, confirmation, confirm);
|
case MarketConfirmation market:
|
||||||
|
result = await MaClient.SendConfirmation(mafile, market, confirm);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = await MaClient.SendConfirmation(mafile, confirmation, confirm);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -96,13 +105,49 @@ public partial class MainVM //Confirmations
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result)
|
if (!result)
|
||||||
{
|
|
||||||
Confirmations.Remove(confirmation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
SnackbarController.SendSnackbar(GetLocalization("ConfirmationError"));
|
SnackbarController.SendSnackbar(GetLocalization("ConfirmationError"));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateConfirmationState(confirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateConfirmationState(Confirmation confirmation)
|
||||||
|
{
|
||||||
|
if (confirmation is MarketConfirmation item)
|
||||||
|
{
|
||||||
|
var parent = Confirmations
|
||||||
|
.OfType<MarketMultiConfirmation>()
|
||||||
|
.FirstOrDefault(p => p.Confirmations.Contains(item));
|
||||||
|
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
Confirmations.Remove(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Confirmations.Remove(item);
|
||||||
|
|
||||||
|
if (parent.Confirmations.Count == 0)
|
||||||
|
{
|
||||||
|
Confirmations.Remove(parent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.Confirmations.Count == 1)
|
||||||
|
{
|
||||||
|
var remaining = parent.Confirmations[0];
|
||||||
|
var idx = Confirmations.IndexOf(parent);
|
||||||
|
Confirmations[idx] = remaining;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.Time = parent.Confirmations.FirstOrDefault()?.Time ?? parent.Time;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Confirmations.Remove(confirmation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -410,6 +410,34 @@
|
|||||||
"zh": "购买",
|
"zh": "购买",
|
||||||
"ua": "Покупка",
|
"ua": "Покупка",
|
||||||
"fr": "Acheter"
|
"fr": "Acheter"
|
||||||
|
},
|
||||||
|
"ConfirmAll": {
|
||||||
|
"en": "Confirm all",
|
||||||
|
"ru": "Подтвердить все",
|
||||||
|
"zh": "全部确认",
|
||||||
|
"ua": "Підтвердити все",
|
||||||
|
"fr": "Tout confirmer"
|
||||||
|
},
|
||||||
|
"CancelAll": {
|
||||||
|
"en": "Cancel all",
|
||||||
|
"ru": "Отклонить все",
|
||||||
|
"zh": "全部取消",
|
||||||
|
"ua": "Відхилити все",
|
||||||
|
"fr": "Tout annuler"
|
||||||
|
},
|
||||||
|
"ConfirmOne": {
|
||||||
|
"en": "Confirm this item",
|
||||||
|
"ru": "Подтвердить этот предмет",
|
||||||
|
"zh": "确认此物品",
|
||||||
|
"ua": "Підтвердити цей предмет",
|
||||||
|
"fr": "Confirmer cet objet"
|
||||||
|
},
|
||||||
|
"CancelOne": {
|
||||||
|
"en": "Cancel this item",
|
||||||
|
"ru": "Отклонить этот предмет",
|
||||||
|
"zh": "取消此物品",
|
||||||
|
"ua": "Відхилити цей предмет",
|
||||||
|
"fr": "Annuler cet objet"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CodeCopied": {
|
"CodeCopied": {
|
||||||
|
|||||||
Reference in New Issue
Block a user