diff --git a/Teti/App.xaml b/Teti/App.xaml
index 1c7980a..36a4ccc 100644
--- a/Teti/App.xaml
+++ b/Teti/App.xaml
@@ -2,17 +2,220 @@
x:Class="InstaArchive.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:converters="using:InstaArchive.Converters">
+ xmlns:converters="using:InstaArchive.Converters"
+ RequestedTheme="Dark">
+
+
+
+ #E1306C
+ #F77EA4
+ #C13584
+ #5B51D8
+ #7B72E8
+ #34C759
+ #FF9500
+ #FF3B30
+ #5856D6
+
+
+ #0A0A0A
+ #141414
+ #1C1C1E
+ #2C2C2E
+ #38383A
+
+
+ #FFFFFF
+ #8E8E93
+ #48484A
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Teti/App.xaml.cs b/Teti/App.xaml.cs
index c089317..d1b451d 100644
--- a/Teti/App.xaml.cs
+++ b/Teti/App.xaml.cs
@@ -59,6 +59,7 @@ public partial class App : Application
services.AddTransient();
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
Services = services.BuildServiceProvider();
}
diff --git a/Teti/InstaArchive.csproj b/Teti/InstaArchive.csproj
index d544793..5a0270a 100644
--- a/Teti/InstaArchive.csproj
+++ b/Teti/InstaArchive.csproj
@@ -17,6 +17,12 @@
None
true
+
+
+
+
+
+
@@ -43,6 +49,21 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+
+
diff --git a/Teti/MainWindow.xaml b/Teti/MainWindow.xaml
index 3630eb8..784d056 100644
--- a/Teti/MainWindow.xaml
+++ b/Teti/MainWindow.xaml
@@ -7,18 +7,110 @@
mc:Ignorable="d"
Title="InstaArchive">
-
+
+ IsPaneToggleButtonVisible="True"
+ IsSettingsVisible="False"
+ SelectionChanged="NavView_SelectionChanged"
+ OpenPaneLength="280"
+ CompactModeThresholdWidth="640"
+ CompactPaneLength="56"
+ Background="{StaticResource DarkSurfaceBrush}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/Teti/MainWindow.xaml.cs b/Teti/MainWindow.xaml.cs
index c51265c..959b81b 100644
--- a/Teti/MainWindow.xaml.cs
+++ b/Teti/MainWindow.xaml.cs
@@ -10,9 +10,21 @@ public sealed partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();
+
+ // Set window properties
ExtendsContentIntoTitleBar = true;
SetTitleBar(null);
+ // Set initial size
+ var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(Microsoft.UI.Win32Interop.GetWindowIdFromWindow(
+ WinRT.Interop.WindowNative.GetWindowHandle(this)));
+
+ if (appWindow != null)
+ {
+ appWindow.Resize(new Windows.Graphics.SizeInt32(1400, 900));
+ }
+
+ // Navigate to Dashboard
ContentFrame.Navigate(typeof(DashboardPage));
}
@@ -25,11 +37,12 @@ public sealed partial class MainWindow : Window
{
"Dashboard" => typeof(DashboardPage),
"Targets" => typeof(TargetsPage),
+ "MediaBrowser" => typeof(MediaBrowserPage),
"Settings" => typeof(SettingsPage),
_ => null
};
- if (pageType != null)
+ if (pageType != null && ContentFrame.CurrentSourcePageType != pageType)
{
ContentFrame.Navigate(pageType);
}
diff --git a/Teti/Properties/launchSettings.json b/Teti/Properties/launchSettings.json
new file mode 100644
index 0000000..43ad452
--- /dev/null
+++ b/Teti/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "WSL": {
+ "commandName": "WSL2",
+ "distributionName": ""
+ },
+ "InstaArchive": {
+ "commandName": "Project"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Teti/Teti.csproj b/Teti/Teti.csproj
new file mode 100644
index 0000000..e69de29
diff --git a/Teti/ViewModels/MediaBrowserViewModel.cs b/Teti/ViewModels/MediaBrowserViewModel.cs
new file mode 100644
index 0000000..9cf2af3
--- /dev/null
+++ b/Teti/ViewModels/MediaBrowserViewModel.cs
@@ -0,0 +1,134 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+using InstaArchive.Models;
+using InstaArchive.Repositories;
+
+namespace InstaArchive.ViewModels;
+
+public partial class MediaBrowserViewModel : ObservableObject
+{
+ private readonly FileBasedMediaRepository _mediaRepository;
+ private readonly FileBasedUserRepository _userRepository;
+
+ [ObservableProperty]
+ private ObservableCollection allMedia = new();
+
+ [ObservableProperty]
+ private ObservableCollection filteredMedia = new();
+
+ [ObservableProperty]
+ private ObservableCollection allUsers = new();
+
+ [ObservableProperty]
+ private InstagramUser? selectedUserFilter;
+
+ [ObservableProperty]
+ private string searchQuery = string.Empty;
+
+ [ObservableProperty]
+ private MediaItem? selectedMedia;
+
+ public MediaBrowserViewModel(
+ FileBasedMediaRepository mediaRepository,
+ FileBasedUserRepository userRepository)
+ {
+ _mediaRepository = mediaRepository;
+ _userRepository = userRepository;
+
+ _ = LoadDataAsync();
+ }
+
+ partial void OnSearchQueryChanged(string value)
+ {
+ FilterMedia();
+ }
+
+ partial void OnSelectedUserFilterChanged(InstagramUser? value)
+ {
+ FilterMedia();
+ }
+
+ private void FilterMedia()
+ {
+ var filtered = AllMedia.AsEnumerable();
+
+ // Filter by search query
+ if (!string.IsNullOrWhiteSpace(SearchQuery))
+ {
+ var query = SearchQuery.ToLower();
+ filtered = filtered.Where(m =>
+ m.FileName.ToLower().Contains(query) ||
+ m.Caption?.ToLower().Contains(query) == true
+ );
+ }
+
+ // Filter by user
+ if (SelectedUserFilter != null)
+ {
+ filtered = filtered.Where(m => m.UserId == SelectedUserFilter.UserId);
+ }
+
+ FilteredMedia.Clear();
+ foreach (var item in filtered)
+ {
+ FilteredMedia.Add(item);
+ }
+ }
+
+ [RelayCommand]
+ private async Task RefreshAsync()
+ {
+ await LoadDataAsync();
+ }
+
+ [RelayCommand]
+ private void OpenMedia(MediaItem media)
+ {
+ if (media == null)
+ {
+ return;
+ }
+
+ // Open media in default application
+ _ = Windows.System.Launcher.LaunchUriAsync(
+ new System.Uri($"file:///{media.LocalPath}")
+ );
+ }
+
+ [RelayCommand]
+ private async Task DeleteMediaAsync(MediaItem media)
+ {
+ if (media == null)
+ {
+ return;
+ }
+
+ // TODO: Implement delete confirmation and deletion
+ AllMedia.Remove(media);
+ FilterMedia();
+ await Task.CompletedTask;
+ }
+
+ private async Task LoadDataAsync()
+ {
+ var mediaList = await _mediaRepository.GetAllMediaAsync();
+ var userList = await _userRepository.GetAllUsersAsync();
+
+ AllMedia.Clear();
+ foreach (var item in mediaList.OrderByDescending(m => m.DownloadedAt))
+ {
+ AllMedia.Add(item);
+ }
+
+ AllUsers.Clear();
+ foreach (var user in userList)
+ {
+ AllUsers.Add(user);
+ }
+
+ FilterMedia();
+ }
+}
diff --git a/Teti/Views/DashboardPage.xaml b/Teti/Views/DashboardPage.xaml
index 7dbffc0..2f9afb0 100644
--- a/Teti/Views/DashboardPage.xaml
+++ b/Teti/Views/DashboardPage.xaml
@@ -4,42 +4,62 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ Background="{StaticResource DarkBackgroundBrush}">
-
+
+
-
-
+
+
-
+
+
+
+
-
-
-
-
-
-
+
+
@@ -47,78 +67,180 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ IsItemClickEnabled="False"
+ Padding="0">
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -126,26 +248,57 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Teti/Views/MediaBrowserPage.xaml b/Teti/Views/MediaBrowserPage.xaml
new file mode 100644
index 0000000..3c71597
--- /dev/null
+++ b/Teti/Views/MediaBrowserPage.xaml
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Teti/Views/MediaBrowserPage.xaml.cs b/Teti/Views/MediaBrowserPage.xaml.cs
new file mode 100644
index 0000000..b07f569
--- /dev/null
+++ b/Teti/Views/MediaBrowserPage.xaml.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.UI.Xaml.Controls;
+using InstaArchive.ViewModels;
+
+namespace InstaArchive.Views;
+
+public sealed partial class MediaBrowserPage : Page
+{
+ public MediaBrowserViewModel ViewModel { get; }
+
+ public MediaBrowserPage()
+ {
+ InitializeComponent();
+ ViewModel = App.Services.GetRequiredService();
+ DataContext = ViewModel;
+ }
+}
diff --git a/Teti/Views/SettingsPage.xaml b/Teti/Views/SettingsPage.xaml
index 28f377b..417c9d1 100644
--- a/Teti/Views/SettingsPage.xaml
+++ b/Teti/Views/SettingsPage.xaml
@@ -4,28 +4,45 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ Background="{StaticResource DarkBackgroundBrush}">
-
+
-
+
+
+
+
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -35,124 +52,259 @@
+ Margin="0,0,12,0"/>
-
+ VerticalAlignment="Bottom">
+
+
+
+
+
+ IsOn="{x:Bind ViewModel.Settings.EnableDateSubfolders, Mode=TwoWay}"
+ OffContent="I file verranno salvati direttamente nella cartella utente"
+ OnContent="I file verranno organizzati in sottocartelle per data"/>
+ Text="{x:Bind ViewModel.Settings.DateFolderFormat, Mode=TwoWay}"
+ Style="{StaticResource DarkTextBoxStyle}"/>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ IsOn="{x:Bind ViewModel.Settings.EnableMetadataInjection, Mode=TwoWay}"
+ OffContent="I metadati non verranno inseriti nei file"
+ OnContent="I metadati (autore, data, descrizione) verranno inseriti nei file"/>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ Maximum="10"
+ SpinButtonPlacementMode="Compact"
+ Description="Numero massimo di download simultanei"/>
-
+
+
+
+
+
+
+
-
+
+
+ IsOn="{x:Bind ViewModel.Settings.AutoStartMonitoring, Mode=TwoWay}"
+ OffContent="Richiede avvio manuale"
+ OnContent="Il monitoraggio si avvia all'apertura dell'app"/>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ IsOn="{x:Bind ViewModel.Settings.EnableRateLimiting, Mode=TwoWay}"
+ OffContent="Nessuna limitazione (sconsigliato)"
+ OnContent="Le richieste verranno limitate per sicurezza"/>
+ Maximum="1000"
+ SpinButtonPlacementMode="Compact"
+ Description="Numero massimo di richieste all'API di Instagram per ora"/>
-
+
+
+
+
+
+
+
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
+ Padding="32,14">
+
+
+
+
+
diff --git a/Teti/Views/TargetsPage.xaml b/Teti/Views/TargetsPage.xaml
index 4da3111..edb320d 100644
--- a/Teti/Views/TargetsPage.xaml
+++ b/Teti/Views/TargetsPage.xaml
@@ -6,15 +6,16 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:InstaArchive.Models"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ Background="{StaticResource DarkBackgroundBrush}">
-
+
-
+
-
+
@@ -23,143 +24,267 @@
-
+
+
+
+
+
-
-
-
+
+
+
+ Style="{StaticResource CardTitleStyle}"/>
+ Text="{x:Bind ViewModel.NewUserId, Mode=TwoWay}"
+ Style="{StaticResource DarkTextBoxStyle}"/>
+ PlaceholderText="Es. nomeutente"
+ Text="{x:Bind ViewModel.NewUsername, Mode=TwoWay}"
+ Style="{StaticResource DarkTextBoxStyle}"/>
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ SelectionMode="Single"
+ Background="Transparent">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+ Text="{x:Bind ViewModel.SelectedUser.CustomBasePath, Mode=TwoWay}"
+ Style="{StaticResource DarkTextBoxStyle}"/>
-
-