Преглед изворни кода

avalonia: Did some corerepostiroy stuff. Added styling to TabItem and TabControl. Added CacheManager

Kenric Nugteren пре 3 месеци
родитељ
комит
51b350d6ea

+ 196 - 0
InABox.Avalonia/CacheManager.cs

@@ -0,0 +1,196 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace InABox.Avalonia;
+
+public static class CacheManager
+{
+    private static Dictionary<string, DateTime> _expiries = new();
+
+    private static Timer? _timer;
+
+    private static void StartTimer()
+    {
+        if (_timer is not null) return;
+
+        // _timer = new Timer(ProcessCache, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
+    }
+
+    private static void ProcessCache(object? state)
+    {
+        var now = DateTime.Now;
+        var toDelete = _expiries.Where(x => x.Value < now).Select(x => x.Key).ToArray();
+        foreach (var item in toDelete)
+        {
+            try
+            {
+                File.Delete(CacheFileName(item));
+            }
+            catch(Exception e)
+            {
+                MobileLogging.Log(e);
+            }
+        }
+        lock (_expiries)
+        {
+            foreach (var item in toDelete)
+            {
+                _expiries.Remove(item);
+            }
+        }
+    }
+    private static void AddExpiry(string key, DateTime expiry)
+    {
+        // lock (_expiries)
+        // {
+        //     _expiries[key] = expiry;
+        // }
+        // SaveExpiries();
+    }
+
+    private static Guid CacheID { get; set; }
+
+    private static bool IsCached(string key) => File.Exists(CacheFileName(key));
+    
+    private static string CacheFileName(string key) =>
+        Path.Combine(CacheFolder(), key);
+
+    private static string CacheFolder()
+    {
+        var result = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+        if (OperatingSystem.IsWindows())
+        {
+            var assembly = Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName);
+            result = Path.Combine(result, assembly);
+        }
+
+        if (CacheID != Guid.Empty)
+            result = Path.Combine(result, CacheID.ToString());
+        if (!Directory.Exists(result))
+            Directory.CreateDirectory(result);
+        return result;
+    }
+
+    private const string EXPIRY_FILE = "cache_expiry.json";
+
+    private static void LoadExpiries()
+    {
+        var filename = CacheFileName(EXPIRY_FILE);
+        if (File.Exists(filename))
+        {
+            var json = File.ReadAllText(filename);
+            lock (_expiries)
+            {
+                _expiries = Serialization.Deserialize<Dictionary<string, DateTime>>(json) ?? [];
+            }
+        }
+        else
+        {
+            lock (_expiries)
+            {
+                _expiries = [];
+            }
+        }
+    }
+    private static void SaveExpiries()
+    {
+        try
+        {
+            lock (_expiries)
+            {
+                var file = CacheFileName(EXPIRY_FILE);
+                var json = Serialization.Serialize(_expiries);
+                File.WriteAllText(file, json);
+            }
+        }
+        catch (Exception e)
+        {
+            MobileLogging.Log(e);
+        }
+    }
+
+    #region Public Interface
+
+    public static void Initialise(Guid cacheID)
+    {
+        CacheID = cacheID;
+        // LoadExpiries();
+
+        StartTimer();
+    }
+
+    public static bool TryLoadBinary<T>(string key, [NotNullWhen(true)] out T? value, out DateTime lastUpdated)
+        where T : class, ISerializeBinary, new()
+    {
+        var filename = CacheFileName(key);
+        if (File.Exists(filename))
+        {
+            lastUpdated = File.GetLastWriteTime(filename);
+            using var stream = new FileStream(filename, FileMode.Open);
+            value = Serialization.ReadBinary<T>(stream, BinarySerializationSettings.Latest);
+            return true;
+        }
+        else
+        {
+            lastUpdated = default;
+            value = null;
+            return false;
+        }
+    }
+    public static void SaveBinary<T>(string key, T value, DateTime expiry)
+        where T : class, ISerializeBinary, new()
+    {
+        var data = value.WriteBinary(BinarySerializationSettings.Latest);
+        try
+        {
+            var file = CacheFileName(key);
+            File.WriteAllBytes(file, data);
+            AddExpiry(key, expiry);
+        }
+        catch (Exception e)
+        {
+            MobileLogging.Log(e);
+        }
+    }
+
+    public static bool TryLoadJSON<T>(string key, [NotNullWhen(true)] out T? value, out DateTime lastUpdated)
+        where T : class, new()
+    {
+        var filename = CacheFileName(key);
+        if (File.Exists(filename))
+        {
+            lastUpdated = File.GetLastWriteTime(filename);
+            var json = File.ReadAllText(filename);
+            value = Serialization.Deserialize<T>(json);
+            return value is not null;
+        }
+        else
+        {
+            lastUpdated = default;
+            value = null;
+            return false;
+        }
+    }
+    public static void SaveJSON<T>(string key, T value, DateTime expiry)
+        where T : class, new()
+    {
+        try
+        {
+            var file = CacheFileName(key);
+            var json = Serialization.Serialize(value);
+            File.WriteAllText(file, json);
+            AddExpiry(key, expiry);
+        }
+        catch (Exception e)
+        {
+            MobileLogging.Log(e);
+        }
+    }
+
+    #endregion
+}

+ 23 - 41
InABox.Avalonia/DataModels/CoreRepository.cs

@@ -227,6 +227,8 @@ namespace InABox.Avalonia
         
         
         private void DoRefresh(bool force)
         private void DoRefresh(bool force)
         {
         {
+            // force = force || Host.Status == ConnectionStatus.Connected;
+
             var selectedIDs = SelectedItems.Select(x => x.ID).ToHashSet();
             var selectedIDs = SelectedItems.Select(x => x.ID).ToHashSet();
             Items.Clear();
             Items.Clear();
 
 
@@ -585,53 +587,45 @@ namespace InABox.Avalonia
         
         
         protected bool LoadFromStorage()
         protected bool LoadFromStorage()
         {
         {
-            
             var filterFileName = FilterFileName();
             var filterFileName = FilterFileName();
-            if (!string.IsNullOrWhiteSpace(filterFileName))
+            if (!filterFileName.IsNullOrWhiteSpace())
             {
             {
-                filterFileName = CacheFileName(filterFileName);
-                if (File.Exists(filterFileName))
+                if(CacheManager.TryLoadJSON<ObservableCollection<CoreRepositoryFilter>>(filterFileName, out var filters, out var _))
                 {
                 {
-                    var json = File.ReadAllText(filterFileName);
-                    var filters = Serialization.Deserialize<ObservableCollection<CoreRepositoryFilter>>(json) ?? new ObservableCollection<CoreRepositoryFilter>();
                     AvailableFilters.ReplaceRange(filters);
                     AvailableFilters.ReplaceRange(filters);
                     Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(FiltersVisible)));
                     Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(FiltersVisible)));
                 }
                 }
             }
             }
 
 
             var dataFileName = DataFileName();
             var dataFileName = DataFileName();
-            if (String.IsNullOrWhiteSpace(dataFileName))
+            if (dataFileName.IsNullOrWhiteSpace())
             {
             {
                 InitializeTables();
                 InitializeTables();
                 return true;
                 return true;
             }
             }
-            
-            dataFileName = CacheFileName(dataFileName);
-            if (File.Exists(dataFileName))
+
+            if(CacheManager.TryLoadBinary<QueryStorage>(dataFileName, out var storage, out var lastUpdated))
             {
             {
-                LastUpdated = File.GetLastWriteTime(dataFileName);
-                using (var stream = new FileStream(dataFileName, FileMode.Open))
+                LastUpdated = lastUpdated;
+                var defs = _query.Definitions();
+                foreach (var key in defs.Keys)
                 {
                 {
-                    QueryStorage storage = Serialization.ReadBinary<QueryStorage>(stream,
-                        BinarySerializationSettings.Latest);
-                    var defs = _query.Definitions();
-                    foreach (var key in defs.Keys)
+                    var keyStr = key.ToString() ?? "";
+                    if(!storage.TryGet(keyStr, out var table))
                     {
                     {
-                        var keyStr = key.ToString() ?? "";
-                        if(!storage.TryGet(keyStr, out var table))
-                        {
-                            table = InitializeTable(defs[key]);
-                        }
-                        var queryDef = _query.Definitions()[key];
-                        if (CheckColumns(table, queryDef.Type, queryDef.Columns))
-                            _query.Set(key, table);
-                        else
-                            return false;
+                        table = InitializeTable(defs[key]);
                     }
                     }
+                    var queryDef = _query.Definitions()[key];
+                    if (CheckColumns(table, queryDef.Type, queryDef.Columns))
+                        _query.Set(key, table);
+                    else
+                        return false;
                 }
                 }
             }
             }
             else
             else
+            {
                 InitializeTables();
                 InitializeTables();
+            }
             
             
             return true;
             return true;
         }
         }
@@ -654,30 +648,18 @@ namespace InABox.Avalonia
             var filterFileName = FilterFileName();
             var filterFileName = FilterFileName();
             if (!string.IsNullOrWhiteSpace(filterFileName))
             if (!string.IsNullOrWhiteSpace(filterFileName))
             {
             {
-                filterFileName = CacheFileName(filterFileName);
-                var json = Serialization.Serialize(AvailableFilters);
-                File.WriteAllText(filterFileName,json);
+                CacheManager.SaveJSON(filterFileName, AvailableFilters, DateTime.MaxValue);
             }
             }
             
             
             var dataFileName = DataFileName();
             var dataFileName = DataFileName();
-            if (String.IsNullOrWhiteSpace(dataFileName))
+            if (dataFileName.IsNullOrWhiteSpace())
                 return;
                 return;
             
             
             QueryStorage storage = new QueryStorage();
             QueryStorage storage = new QueryStorage();
             var results = _query.Results();
             var results = _query.Results();
             foreach (var key in results.Keys)
             foreach (var key in results.Keys)
                 storage.Set(key.ToString() ?? "", results[key]);
                 storage.Set(key.ToString() ?? "", results[key]);
-            var data = storage.WriteBinary(BinarySerializationSettings.Latest);
-            try
-            {
-                var file = CacheFileName(dataFileName);
-                File.WriteAllBytes(file,data);
-            }
-            catch (Exception e)
-            {
-                MobileLogging.Log(e);
-            }
-
+            CacheManager.SaveBinary(dataFileName, storage, DateTime.MaxValue);
         }
         }
         
         
         #endregion
         #endregion

+ 70 - 0
InABox.Avalonia/Theme/Classes/TabItem.axaml

@@ -0,0 +1,70 @@
+<Styles xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+	<Style Selector="TabControl">
+		<Setter Property="ItemsPanel">
+			<Setter.Value>
+				<ItemsPanelTemplate>
+					<UniformGrid Rows="1"/>
+				</ItemsPanelTemplate>
+			</Setter.Value>
+		</Setter>
+        <Setter Property="Template">
+			<ControlTemplate>
+				<Border BorderBrush="{TemplateBinding BorderBrush}"
+						BorderThickness="{TemplateBinding BorderThickness}"
+						CornerRadius="{TemplateBinding CornerRadius}"
+						Background="{TemplateBinding Background}"
+						HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
+						VerticalAlignment="{TemplateBinding VerticalAlignment}">
+					<DockPanel>
+						<Border DockPanel.Dock="{TemplateBinding TabStripPlacement}"
+								Name="PART_HeaderBorder">
+							<ItemsPresenter Name="PART_ItemsPresenter"
+											ItemsPanel="{TemplateBinding ItemsPanel}"/>
+						</Border>
+						<ContentPresenter Name="PART_SelectedContentHost"
+										  Margin="{TemplateBinding Padding}"
+										  HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
+										  VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+										  Content="{TemplateBinding SelectedContent}"
+										  ContentTemplate="{TemplateBinding SelectedContentTemplate}" />
+					</DockPanel>
+				</Border>
+			</ControlTemplate>
+        </Setter>
+	</Style>
+	<Style Selector="TabControl Border#PART_HeaderBorder">
+		<Setter Property="Background" Value="{DynamicResource PrsMenuBackground}"/>
+		<Setter Property="BorderBrush" Value="{DynamicResource PrsMenuBackground}"/>
+		<Setter Property="BorderThickness" Value="2"/>
+		<Setter Property="CornerRadius" Value="{DynamicResource PrsCornerRadius}"/>
+	</Style>
+	<Style Selector="TabItem">
+		<Setter Property="Height" Value="30"/>
+		<Setter Property="MinHeight" Value="0"/>
+		<Setter Property="FontSize" Value="{DynamicResource PrsFontSizeSmall}"/>
+		
+		<Setter Property="HorizontalContentAlignment" Value="Center"/>
+		
+		<Setter Property="Background" Value="{DynamicResource PrsMenuBackground}"/>
+		<Setter Property="Foreground" Value="White"/>
+		<Setter Property="CornerRadius" Value="{DynamicResource PrsCornerRadius}"/>
+	</Style>
+	
+	<Style Selector="TabItem:pointerover /template/ Border#PART_LayoutRoot">
+		<Setter Property="Background" Value="{DynamicResource PrsMenuBackground}"/>
+		<Setter Property="TextElement.Foreground" Value="White"/>
+	</Style>
+	
+	<Style Selector="TabItem:selected">
+		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
+	</Style>
+	<Style Selector="TabItem:selected:pointerover /template/ Border#PART_LayoutRoot">
+		<Setter Property="Background" Value="{DynamicResource PrsTileBackground}"/>
+		<Setter Property="TextElement.Foreground" Value="Black"/>
+	</Style>
+	
+	<Style Selector="TabItem:selected /template/ Border#PART_SelectedPipe">
+		<Setter Property="IsVisible" Value="False"/>
+	</Style>
+</Styles>

+ 1 - 0
InABox.Avalonia/Theme/Styles.axaml

@@ -11,4 +11,5 @@
 	<StyleInclude Source="/Theme/Classes/Separator.axaml" />
 	<StyleInclude Source="/Theme/Classes/Separator.axaml" />
 	<StyleInclude Source="/Theme/Classes/ListBox.axaml" />
 	<StyleInclude Source="/Theme/Classes/ListBox.axaml" />
 	<StyleInclude Source="/Theme/Classes/TabStrip.axaml" />
 	<StyleInclude Source="/Theme/Classes/TabStrip.axaml" />
+	<StyleInclude Source="/Theme/Classes/TabItem.axaml" />
 </Styles>
 </Styles>

+ 2 - 2
InABox.Core/CoreObservableCollection.cs

@@ -587,7 +587,7 @@ namespace InABox.Core
             else
             else
             {
             {
                 // Raises the CollectionChanged event on the creator thread
                 // Raises the CollectionChanged event on the creator thread
-                _synchronizationContext.Send(RaiseCollectionChanged, e);
+                _synchronizationContext.Post(RaiseCollectionChanged, e);
             }
             }
         }
         }
 
 
@@ -644,7 +644,7 @@ namespace InABox.Core
             else
             else
             {
             {
                 // Raises the PropertyChanged event on the creator thread
                 // Raises the PropertyChanged event on the creator thread
-                _synchronizationContext.Send(RaisePropertyChanged, e);
+                _synchronizationContext.Post(RaisePropertyChanged, e);
             }
             }
         }
         }