From f58b7db902bcee25612d49989b35630b7c8913f9 Mon Sep 17 00:00:00 2001 From: reddyashish <43763136+reddyashish@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:13:49 -0700 Subject: [PATCH] [Cherrypick] DYN-7154: missing thumbnail sample files (#15326) (#15334) Co-authored-by: Deyan Nenov --- src/DynamoCoreWpf/Controls/StartPage.xaml.cs | 82 +++++++++++++++----- src/DynamoCoreWpf/DynamoCoreWpf.csproj | 2 +- src/DynamoCoreWpf/PublicAPI.Unshipped.txt | 4 + test/DynamoCoreWpfTests/HomePageTests.cs | 17 ++++ test/core/Home.dyn | 70 +++++++++++++++-- 5 files changed, 148 insertions(+), 27 deletions(-) diff --git a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs index 8126d825b2f..b539922593b 100644 --- a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs +++ b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs @@ -57,12 +57,12 @@ public enum Action ExternalUrl } - internal StartPageListItem(string caption) + protected internal StartPageListItem(string caption) { this.Caption = caption; } - internal StartPageListItem(string caption, string iconPath) + protected internal StartPageListItem(string caption, string iconPath) { this.Caption = caption; this.icon = LoadBitmapImage(iconPath); @@ -95,7 +95,7 @@ public Visibility IconVisibility #region Private Class Helper Methods - private BitmapImage LoadBitmapImage(string iconPath) + protected BitmapImage LoadBitmapImage(string iconPath) { var format = @"pack://application:,,,/DynamoCoreWpf;component/UI/Images/StartPage/{0}"; iconPath = string.Format(format, iconPath); @@ -127,8 +127,8 @@ internal StartPageViewModel(DynamoViewModel dynamoViewModel, bool isFirstRun) this.isFirstRun = isFirstRun; this.recentFiles = new ObservableCollection(); - sampleFiles = new ObservableCollection(); - backupFiles = new ObservableCollection(); + this.sampleFiles = new ObservableCollection(); + this.backupFiles = new ObservableCollection(); #region File Operations @@ -214,6 +214,7 @@ internal StartPageViewModel(DynamoViewModel dynamoViewModel, bool isFirstRun) RefreshBackupFileList(dvm.Model.PreferenceSettings.BackupFiles); dvm.RecentFiles.CollectionChanged += OnRecentFilesChanged; } + internal void WalkDirectoryTree(System.IO.DirectoryInfo root, SampleFileEntry rootProperty) { try @@ -248,14 +249,24 @@ internal void WalkDirectoryTree(System.IO.DirectoryInfo root, SampleFileEntry ro { sampleFolderPath = Path.GetDirectoryName(file.FullName); } + // Add each file under the root directory property list. - rootProperty.AddChildSampleFile(new SampleFileEntry(file.Name, file.FullName)); + var properties = GetFileProperties(file.FullName); + + rootProperty.AddChildSampleFile(new SampleFileEntry( + file.Name, + file.FullName, + properties.thumbnail, + properties.author, + properties.description, + properties.date)); } } } - catch (Exception) + catch (Exception ex) { // Perhaps some permission problems? + DynamoViewModel.Model.Logger.Log("Error loading sample file: " + ex.StackTrace); } } @@ -386,22 +397,17 @@ private void RefreshFileList(ObservableCollection files, var caption = Path.GetFileNameWithoutExtension(filePath); // deserializes the file only once - var jsonObject = DeserializeJsonFile(filePath); - var description = jsonObject != null ? GetGraphDescription(jsonObject) : string.Empty; - var thumbnail = jsonObject != null ? GetGraphThumbnail(jsonObject) : string.Empty; - var author = jsonObject != null ? GetGraphAuthor(jsonObject) : Resources.DynamoXmlFileFormat; - - var date = DynamoUtilities.PathHelper.GetDateModified(filePath); + var properties = GetFileProperties(filePath); files.Add(new StartPageListItem(caption) { ContextData = filePath, ToolTip = filePath, SubScript = subScript, - Description = description, - Thumbnail = thumbnail, - Author = author, - DateModified = date, + Description = properties.description, + Thumbnail = properties.thumbnail, + Author = properties.author, + DateModified = properties.date, ClickAction = StartPageListItem.Action.FilePath, }); @@ -499,6 +505,33 @@ private void HandleExternalUrl(StartPageListItem item) System.Diagnostics.Process.Start(new ProcessStartInfo(item.ContextData) { UseShellExecute = true }); } + /// + /// Attempts to deserialize a dynamo graph file and extract metadata from it + /// + /// The file path to the dynamo file + /// + internal (string description, string thumbnail, string author, string date) GetFileProperties(string filePath) + { + if (!filePath.ToLower().EndsWith(".dyn") && !filePath.ToLower().EndsWith(".dyf")) return (null, null, null, null); + + try + { + var jsonObject = DeserializeJsonFile(filePath); + var description = jsonObject != null ? GetGraphDescription(jsonObject) : string.Empty; + var thumbnail = jsonObject != null ? GetGraphThumbnail(jsonObject) : string.Empty; + var author = jsonObject != null ? GetGraphAuthor(jsonObject) : Resources.DynamoXmlFileFormat; + var date = DynamoUtilities.PathHelper.GetDateModified(filePath); + + return (description, thumbnail, author, date); + } + catch (Exception ex) + { + DynamoViewModel.Model.Logger.Log("Error deserializing dynamo graph file: " + ex.StackTrace); + return (null, null, null, null); + } + + } + #endregion } @@ -623,15 +656,28 @@ private void StartPage_OnDrop(object sender, DragEventArgs e) #endregion } - public class SampleFileEntry + public class SampleFileEntry : StartPageListItem { List childSampleFiles = null; public SampleFileEntry(string name, string path) + : base(name) { this.FileName = name; this.FilePath = path; } + + public SampleFileEntry(string name, string path, string thumbnail, string author, string description, string dateModified) + : base(name) + { + this.FileName = name; + this.FilePath = path; + this.Thumbnail = thumbnail; + this.Author = author; + this.Description = description; + this.DateModified = dateModified; + } + public void AddChildSampleFile(SampleFileEntry childSampleFile) { if (null == childSampleFiles) diff --git a/src/DynamoCoreWpf/DynamoCoreWpf.csproj b/src/DynamoCoreWpf/DynamoCoreWpf.csproj index 9f1c2ed4a3c..19ad7f4d2d7 100644 --- a/src/DynamoCoreWpf/DynamoCoreWpf.csproj +++ b/src/DynamoCoreWpf/DynamoCoreWpf.csproj @@ -53,7 +53,7 @@ - + diff --git a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt index f03338cf293..bf17d2be88d 100644 --- a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt +++ b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt @@ -1427,6 +1427,7 @@ Dynamo.UI.Controls.SampleFileEntry.Children.get -> System.Collections.Generic.IE Dynamo.UI.Controls.SampleFileEntry.FileName.get -> string Dynamo.UI.Controls.SampleFileEntry.FilePath.get -> string Dynamo.UI.Controls.SampleFileEntry.SampleFileEntry(string name, string path) -> void +Dynamo.UI.Controls.SampleFileEntry.SampleFileEntry(string name, string path, string thumbnail, string author, string description, string dateModified) -> void Dynamo.UI.Controls.ShortcutBarItem Dynamo.UI.Controls.ShortcutBarItem.ImgDisabledSource.get -> string Dynamo.UI.Controls.ShortcutBarItem.ImgDisabledSource.set -> void @@ -1465,6 +1466,9 @@ Dynamo.UI.Controls.StartPageListItem.DateModified.set -> void Dynamo.UI.Controls.StartPageListItem.Description.get -> string Dynamo.UI.Controls.StartPageListItem.Icon.get -> System.Windows.Media.ImageSource Dynamo.UI.Controls.StartPageListItem.IconVisibility.get -> System.Windows.Visibility +Dynamo.UI.Controls.StartPageListItem.LoadBitmapImage(string iconPath) -> System.Windows.Media.Imaging.BitmapImage +Dynamo.UI.Controls.StartPageListItem.StartPageListItem(string caption) -> void +Dynamo.UI.Controls.StartPageListItem.StartPageListItem(string caption, string iconPath) -> void Dynamo.UI.Controls.StartPageListItem.SubScript.get -> string Dynamo.UI.Controls.StartPageListItem.SubScript.set -> void Dynamo.UI.Controls.StartPageListItem.Thumbnail.get -> string diff --git a/test/DynamoCoreWpfTests/HomePageTests.cs b/test/DynamoCoreWpfTests/HomePageTests.cs index 2bfa3e36291..e9f36cf1409 100644 --- a/test/DynamoCoreWpfTests/HomePageTests.cs +++ b/test/DynamoCoreWpfTests/HomePageTests.cs @@ -617,6 +617,23 @@ public void CanOpenGraphOnDragAndDrop() } #endregion + [Test] + public void TestDeserializeDynamoGraphProperties() + { + // Arrange + var filePath = Path.Combine(GetTestDirectory(ExecutingDirectory), @"core\Home.dyn"); + var vm = View.DataContext as DynamoViewModel; + var startPage = new StartPageViewModel(vm, true); + + // Act + var properties = startPage.GetFileProperties(filePath); + + // Assert + Assert.AreEqual(properties.description, "Test description"); + Assert.AreEqual(properties.author, "John Doe"); + Assert.IsFalse(string.IsNullOrEmpty(properties.thumbnail)); + } + #region helpers /// diff --git a/test/core/Home.dyn b/test/core/Home.dyn index 4de92069e83..426aa2e92e2 100644 --- a/test/core/Home.dyn +++ b/test/core/Home.dyn @@ -1,8 +1,62 @@ - - - - - - - - \ No newline at end of file +{ + "Uuid": "1b4db7eb-4057-5ddf-91e0-36dec72071f5", + "IsCustomNode": false, + "Description": "Test description", + "Name": "Home", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [], + "Connectors": [], + "Dependencies": [], + "NodeLibraryDependencies": [], + "EnableLegacyPolyCurveBehavior": null, + "Thumbnail": "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fwa+Btj8MPitZ+KoJ9X1G4aKS2NnFo8drEA8J+YKpAH3BnA6k55r6Yk1jXtRBisNEOmseDdatLGUUEcMscLszkf3WaP/AHhXM+D/AIbap4f8RWl/c3dtJDCsgZI2YliykA8r2z616RWsb21IZkaH4dj0iSe6mnkv9UugouL6cAO4XO1FA4SNcnCLxyzHLMzNr0UVQgooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA4j4jeEdQ8T3GmSWIiIt0mV/Mfb94x4xx/smuQ/wCFV69/ct/+/v8A9aiiocE3dlKTQf8ACq9e/uW//f3/AOtUF58JfENxCyIbZNysCDJnORjHTiiil7OI+ZnttFFFaEBRRRQAUUUUAFFFFABVe+0601OIRXlrDdxq24JPGHAOCM4I68n86KKAKP8Awieh/wDQG0//AMBY/wDCj/hE9D/6A2n/APgLH/hRRSsh3Yf8Inof/QG0/wD8BY/8Kns/D+l6fMJrXTbS2mAwJIYERhn3AooosBfooopiCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9k=", + "GraphDocumentationURL": null, + "ExtensionWorkspaceData": [ + { + "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", + "Name": "Properties", + "Version": "3.3", + "Data": {} + } + ], + "Author": "John Doe", + "Linting": { + "activeLinter": "None", + "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", + "warningCount": 0, + "errorCount": 0 + }, + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": false, + "IsVisibleInDynamoLibrary": true, + "Version": "3.3.0.5104", + "RunType": "Manual", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "_Background Preview", + "EyeX": -17.0, + "EyeY": 24.0, + "EyeZ": 50.0, + "LookX": 12.0, + "LookY": -13.0, + "LookZ": -58.0, + "UpX": 0.0, + "UpY": 1.0, + "UpZ": 0.0 + }, + "ConnectorPins": [], + "NodeViews": [], + "Annotations": [], + "X": 0.0, + "Y": 0.0, + "Zoom": 1.0 + } +} \ No newline at end of file