diff --git a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs index 10c0b985bbd..9a73db36845 100644 --- a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs +++ b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs @@ -819,28 +819,46 @@ public override bool CanRead public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var ws = (WorkspaceModel)value; - bool isCustomNode = value is CustomNodeWorkspaceModel; + var customWorkspace = ws as CustomNodeWorkspaceModel; + var isCustomNode = customWorkspace != null; + var homeWorkspace = ws as HomeWorkspaceModel; + var isHomeWsModel = homeWorkspace != null; writer.WriteStartObject(); - writer.WritePropertyName("Uuid"); + + // Write the start page graph properties first + + // Graph Author + writer.WritePropertyName(nameof(WorkspaceModel.Author)); + writer.WriteValue(ws.Author); + + // Description + writer.WritePropertyName("Description"); if (isCustomNode) - writer.WriteValue((ws as CustomNodeWorkspaceModel).CustomNodeId.ToString()); + writer.WriteValue(customWorkspace.Description); else - writer.WriteValue(ws.Guid.ToString()); + writer.WriteValue(ws.Description); + + // Thumbnail + if (!isCustomNode && isHomeWsModel) + { + writer.WritePropertyName(nameof(HomeWorkspaceModel.Thumbnail)); + writer.WriteValue(homeWorkspace.Thumbnail); + } + + writer.WritePropertyName("Uuid"); + writer.WriteValue(isCustomNode ? + customWorkspace.CustomNodeId.ToString(): + ws.Guid.ToString()); + // TODO: revisit IsCustomNode during DYN/DYF convergence writer.WritePropertyName("IsCustomNode"); - writer.WriteValue(value is CustomNodeWorkspaceModel ? true : false); + writer.WriteValue(isCustomNode); if (isCustomNode) { writer.WritePropertyName("Category"); writer.WriteValue(((CustomNodeWorkspaceModel)value).Category); } - // Description - writer.WritePropertyName("Description"); - if (isCustomNode) - writer.WriteValue(((CustomNodeWorkspaceModel)ws).Description); - else - writer.WriteValue(ws.Description); writer.WritePropertyName("Name"); writer.WriteValue(ws.Name); @@ -884,7 +902,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } writer.WriteEndArray(); - // Join NodeLibraryDependencies & NodeLocalDefinitions and serialze them. + // Join NodeLibraryDependencies & NodeLocalDefinitions and serialize them. writer.WritePropertyName(WorkspaceReadConverter.NodeLibraryDependenciesPropString); IEnumerable referencesList = ws.NodeLibraryDependencies; @@ -915,29 +933,21 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s serializer.Serialize(writer, referencesList); - if (!isCustomNode && ws is HomeWorkspaceModel hws) + if (!isCustomNode && isHomeWsModel) { // EnableLegacyPolyCurveBehavior writer.WritePropertyName(nameof(HomeWorkspaceModel.EnableLegacyPolyCurveBehavior)); - serializer.Serialize(writer, hws.EnableLegacyPolyCurveBehavior); - - // Thumbnail - writer.WritePropertyName(nameof(HomeWorkspaceModel.Thumbnail)); - writer.WriteValue(hws.Thumbnail); + serializer.Serialize(writer, homeWorkspace.EnableLegacyPolyCurveBehavior); // GraphDocumentaionLink writer.WritePropertyName(nameof(HomeWorkspaceModel.GraphDocumentationURL)); - writer.WriteValue(hws.GraphDocumentationURL); + writer.WriteValue(homeWorkspace.GraphDocumentationURL); // ExtensionData writer.WritePropertyName(WorkspaceReadConverter.EXTENSION_WORKSPACE_DATA); - serializer.Serialize(writer, hws.ExtensionData); + serializer.Serialize(writer, homeWorkspace.ExtensionData); } - // Graph Author - writer.WritePropertyName(nameof(WorkspaceModel.Author)); - writer.WriteValue(ws.Author); - // Linter if(!(ws.linterManager is null)) { diff --git a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs index 1ee8f9944de..3fe32a3d879 100644 --- a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs +++ b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs @@ -465,51 +465,81 @@ private void RefreshFileList(ObservableCollection files, } } - private Dictionary DeserializeJsonFile(string filePath) + private readonly string[] jsonKeys = { "Description", "Thumbnail", "Author" }; + private readonly Dictionary propertyLookup = []; + private static DefaultJsonNameTable propertyTable = null; + + private class GraphData { - if (DynamoUtilities.PathHelper.isValidJson(filePath, out string jsonString, out Exception ex)) - { - return JsonConvert.DeserializeObject>(jsonString); - } - else + public string Author = ""; + public string Description = ""; + public string Thumbnail = ""; + } + + private GraphData DeserializeJsonFileTest(string filePath) + { + return JsonConvert.DeserializeObject(File.ReadAllText(filePath)); + } + + private Dictionary DeserializeJsonFile(string filePath) + { + if (propertyTable == null) { - if(ex is JsonReaderException) + propertyTable = new DefaultJsonNameTable(); + foreach (var pn in jsonKeys) { - DynamoViewModel.Model.Logger.Log("File is not a valid json format."); + propertyLookup[pn] = propertyTable.Add(pn); } - else - { - DynamoViewModel.Model.Logger.Log("File is not valid: " + ex.StackTrace); - } - return null; } - } - private const string BASE64PREFIX = "data:image/png;base64,"; - - private string GetGraphThumbnail(Dictionary jsonObject) - { - jsonObject.TryGetValue("Thumbnail", out object thumbnail); + try + { + var data = new Dictionary(); + // JsonTextRead will automatically dispose of the stream reader + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + using (var jr = new JsonTextReader(new StreamReader(fs)) { PropertyNameTable = propertyTable }) + { + while(jr.Read()) + { + if (data.Count == jsonKeys.Length) + { + break; + } - if (string.IsNullOrEmpty(thumbnail as string)) return string.Empty; + if (jr.Depth != 1) + { + continue; + } - var base64 = String.Format("{0}{1}", BASE64PREFIX, thumbnail as string); + if (jr.TokenType == JsonToken.PropertyName) + { + foreach (var prop in propertyLookup) + { + if (jr.Value == prop.Value) + { + data[prop.Key] = jr.ReadAsString() ?? ""; + break; + } + } + } + } + } - return base64; + return data; + } + catch + { + DynamoViewModel.Model.Logger.Log("File is not a valid json format."); + return null; + } } - private string GetGraphDescription(Dictionary jsonObject) - { - jsonObject.TryGetValue("Description", out object description); - - return description as string; - } + private const string BASE64PREFIX = "data:image/png;base64,"; - private string GetGraphAuthor(Dictionary jsonObject) + private string GetGraphThumbnail(Dictionary jsonObject) { - jsonObject.TryGetValue("Author", out object author); - - return author as string; + jsonObject.TryGetValue("Thumbnail", out var thumbnail); + return string.IsNullOrEmpty(thumbnail) ? string.Empty : $"{BASE64PREFIX}{thumbnail}"; } private void HandleRegularCommand(StartPageListItem item) @@ -559,9 +589,12 @@ private void HandleExternalUrl(StartPageListItem item) try { var jsonObject = DeserializeJsonFile(filePath); - var description = jsonObject != null ? GetGraphDescription(jsonObject) : string.Empty; + //var test = DeserializeJsonFileTest(filePath); + var description = jsonObject?.TryGetValue("Description", out var description_) == true ? + description_ : string.Empty; + var author = jsonObject?.TryGetValue("Author", out var author_) == true ? + author_ : Resources.DynamoXmlFileFormat; 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); diff --git a/src/DynamoCoreWpf/DynamoCoreWpf.csproj b/src/DynamoCoreWpf/DynamoCoreWpf.csproj index 483b81af869..cc4cf59d78c 100644 --- a/src/DynamoCoreWpf/DynamoCoreWpf.csproj +++ b/src/DynamoCoreWpf/DynamoCoreWpf.csproj @@ -189,7 +189,7 @@ - + diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs index 20505d80a39..44665b33fcc 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs @@ -518,9 +518,9 @@ public ConnectorType ConnectorType } } - private ObservableCollection recentFiles = - new ObservableCollection(); - public ObservableCollection RecentFiles + private SmartObservableCollection recentFiles = + new SmartObservableCollection(); + public SmartObservableCollection RecentFiles { get { return recentFiles; } set @@ -1108,7 +1108,7 @@ protected virtual void UnsubscribeAllEvents() private void InitializeRecentFiles() { - this.RecentFiles = new ObservableCollection(model.PreferenceSettings.RecentFiles); + this.RecentFiles = new SmartObservableCollection(model.PreferenceSettings.RecentFiles); this.RecentFiles.CollectionChanged += (sender, args) => { model.PreferenceSettings.RecentFiles = this.RecentFiles.ToList(); @@ -1814,18 +1814,8 @@ internal void AddToRecentFiles(string path) { if (path == null) return; - if (RecentFiles.Contains(path)) - { - RecentFiles.Remove(path); - } - - RecentFiles.Insert(0, path); - int maxNumRecentFiles = Model.PreferenceSettings.MaxNumRecentFiles; - if (RecentFiles.Count > maxNumRecentFiles) - { - RecentFiles.RemoveRange(maxNumRecentFiles, RecentFiles.Count - maxNumRecentFiles); - } + RecentFiles.PushToFrontAndTrimExcess(path, maxNumRecentFiles); } // Get the nodemodel if a node is present in any open workspace. diff --git a/src/DynamoUtilities/SmartObservableCollection.cs b/src/DynamoUtilities/SmartObservableCollection.cs index c84324b65b1..e81f9fddb72 100644 --- a/src/DynamoUtilities/SmartObservableCollection.cs +++ b/src/DynamoUtilities/SmartObservableCollection.cs @@ -214,5 +214,28 @@ internal void SetCollection(IEnumerable range) } } } + + internal void PushToFrontAndTrimExcess(T item, int maxNumItems = -1) + { + using (DeferCollectionNotification(NotifyCollectionChangedAction.Reset, Items.ToList())) + { + var index = Items.IndexOf(item); + if (index > 0) + { + RemoveAt(index); + } + Insert(0, item); + + if (maxNumItems > 0 && Items.Count > maxNumItems) + { + var toRemove = Items.Count - maxNumItems; + while (toRemove > 0) + { + toRemove--; + RemoveAt(Items.Count - 1); + } + } + } + } } -} \ No newline at end of file +}