├── .gitignore ├── LICENSE.md ├── README.md ├── UIssueTracker.sln ├── UIssueTracker ├── App.config ├── App.xaml ├── App.xaml.cs ├── Classes │ └── Constants.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── UIssueTracker.csproj ├── UserControls │ ├── IssueCard.xaml │ ├── IssueCard.xaml.cs │ ├── IssueDetails.xaml │ ├── IssueDetails.xaml.cs │ ├── IssueHistoryCard.xaml │ └── IssueHistoryCard.xaml.cs ├── packages.config └── uit_icon.ico └── _config.yml /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Satheesh (ryanjon2040) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unreal Issue Tracker 2 | 3 | [![](https://i.imgur.com/BuNtUbw.png)](https://imgur.com/a/cnZP2) 4 | 5 | **Unreal Issue Tracker** is a desktop app developed by **Satheesh (ryanjon2040)** for Unreal Community. This app reads the rss feeds from [Unreal Engine Issues](https://issues.unrealengine.com/) website and has the following features. 6 | 7 | - Up-to-date information about latest bugs and fixes. 8 | - Subscribe to specific issues. 9 | - Keeps a history of viewed issues. 10 | - and more... 11 | 12 | # Features 13 | 14 | ### Main Interface 15 | Get up-to-date information from the Unreal Engine Issues website. Expand an issue to read the description and you can click the link to go to that issue page. 16 | [![](https://i.imgur.com/KTgY0Cy.png)](https://imgur.com/a/cnZP2) 17 | 18 | ### Issue Details 19 | Clicking on the **SHOW DETAILS** button will show current status of that issue with steps to reproduce and a link to AnswerHub or UDN (if available). From here, you can select **MORE INFORMATION** to see which component it affects and for what Engine versions. You can also click **SUBSCRIBE** to follow this event. 20 | [![](https://i.imgur.com/CmD9qdd.png)](https://imgur.com/a/cnZP2) 21 | 22 | ### History 23 | Unreal Issue Tracker will keep a history of all the issues you have visited. 24 | [![](https://i.imgur.com/rAo5ATW.png)](https://imgur.com/a/cnZP2) 25 | 26 | I hope you like this small app :). 27 | 28 | # Open Source 29 | 30 | Unreal Issue Tracker uses the following open source libraries: 31 | 32 | * [MaterialDesignInXamlToolkit](https://github.com/ButchersBoy/MaterialDesignInXamlToolkit) - Google's Material Design in XAML & WPF, for C# & Visual Basic. 33 | * [Feed Reader](https://github.com/codehollow/FeedReader) - C# RSS and ATOM Feed reader library. 34 | * [Html Agility Pack](http://html-agility-pack.net/) - Html Agility Pack (HAP). 35 | * [Newtonsoft JSON](https://www.newtonsoft.com/json) - Popular high-performance JSON framework for .NET 36 | 37 | ### Download 38 | 39 | Check the [Releases](https://github.com/ryanjon2040/Unreal-Issue-Tracker/releases) page to download the binary version of Unreal Issue Tracker. 40 | 41 | License 42 | ---- 43 | 44 | [https://github.com/ryanjon2040/Unreal-Issue-Tracker/blob/master/LICENSE.md](MIT) 45 | 46 | 47 | **Free Software, Hell Yeah!** 48 | -------------------------------------------------------------------------------- /UIssueTracker.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2020 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UIssueTracker", "UIssueTracker\UIssueTracker.csproj", "{D48705BF-338B-4495-AC43-683DA1A687CB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Debug|x64.ActiveCfg = Debug|Any CPU 17 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Debug|x64.Build.0 = Debug|Any CPU 18 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Debug|x86.ActiveCfg = Debug|Any CPU 19 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Debug|x86.Build.0 = Debug|Any CPU 20 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Release|x64.ActiveCfg = Release|Any CPU 21 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Release|x64.Build.0 = Release|Any CPU 22 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Release|x86.ActiveCfg = Release|Any CPU 23 | {D48705BF-338B-4495-AC43-683DA1A687CB}.Release|x86.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {3730186B-DCA8-49A7-AEBC-AC7A655BA8ED} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /UIssueTracker/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /UIssueTracker/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /UIssueTracker/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace UIssueTracker 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UIssueTracker/Classes/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Media; 5 | using Newtonsoft.Json; 6 | using UIssueTracker.UserControls; 7 | using System.Collections.Generic; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace UIssueTracker.Classes 11 | { 12 | public class Constants 13 | { 14 | public static readonly string PRODUCT_NAME = "Unreal Issue Tracker"; 15 | public static readonly string BUG_URL = @"https://issues.unrealengine.com/feed/bugs"; 16 | public static readonly string FIX_URL = @"https://issues.unrealengine.com/feed/fixes"; 17 | 18 | private static readonly string IssueTrackerFileExtension = ".uitf"; 19 | private static readonly string IssueFilesDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\" + PRODUCT_NAME + @"\"; 20 | private static readonly string IssueTrackerHistoryDirectory = IssueFilesDirectory + @"History\"; 21 | private static readonly string IssueTrackerHistoryFile = IssueTrackerHistoryDirectory + "IssueHistory.uithf"; 22 | 23 | public enum IssueResolution 24 | { 25 | Backlogged, 26 | ByDesign, 27 | CannotReproduce, 28 | Duplicate, 29 | Fixed, 30 | NonIssue, 31 | Unresolved, 32 | WontDo, 33 | WontFix, 34 | None 35 | } 36 | 37 | private static void CreateIssueFileLocation() 38 | { 39 | if (Directory.Exists(IssueFilesDirectory) == false) 40 | { 41 | Directory.CreateDirectory(IssueFilesDirectory); 42 | } 43 | } 44 | 45 | private static void CreateHistoryFile() 46 | { 47 | if (File.Exists(IssueTrackerHistoryFile) == false) 48 | { 49 | if (Directory.Exists(IssueTrackerHistoryDirectory) == false) 50 | { 51 | Directory.CreateDirectory(IssueTrackerHistoryDirectory); 52 | } 53 | JObject EmptyJson = new JObject(); 54 | File.WriteAllText(IssueTrackerHistoryFile, EmptyJson.ToString(Formatting.Indented)); 55 | } 56 | } 57 | 58 | public static bool IsSubscribedToIssue(string IssueNumber) 59 | { 60 | return File.Exists(IssueFilesDirectory + IssueNumber + IssueTrackerFileExtension); 61 | } 62 | 63 | public static bool SaveToHistory(IssueDetails issueDetails) 64 | { 65 | CreateHistoryFile(); 66 | HistoryIssue historyIssue = JsonConvert.DeserializeObject(File.ReadAllText(IssueTrackerHistoryFile)); 67 | if (historyIssue.IssueHistory.ContainsKey(GetIssueNumber(issueDetails.IssueURL)) == false) 68 | { 69 | historyIssue.IssueHistory.Add(GetIssueNumber(issueDetails.IssueURL), issueDetails.CurrentIssueTitle); 70 | string serializedJson = JsonConvert.SerializeObject(historyIssue, Formatting.Indented); 71 | File.WriteAllText(IssueTrackerHistoryFile, serializedJson); 72 | return true; 73 | } 74 | return false; 75 | } 76 | 77 | public static bool SubscribeToIssue(IssueDetails issueDetails) 78 | { 79 | CreateIssueFileLocation(); 80 | string IssueNumber = GetIssueNumber(issueDetails.IssueURL); 81 | if (IsSubscribedToIssue(IssueNumber) == false) 82 | { 83 | SubscribeIssue subscribeIssue = new SubscribeIssue(); 84 | subscribeIssue.SubscribedIssueResolution = issueDetails.CurrentIssueStatus; 85 | subscribeIssue.SubscribedIssueTitle = issueDetails.CurrentIssueTitle; 86 | subscribeIssue.SubscribedIssueID = IssueNumber; 87 | subscribeIssue.SubscribedIssueLink = issueDetails.IssueURL; 88 | subscribeIssue.SubscribedIssueDescription = issueDetails.CurrentIssueDescription; 89 | subscribeIssue.SubscribedIssueRepro = issueDetails.CurrentIssueRepro; 90 | subscribeIssue.SubscribedIssueMoreInfo = issueDetails.CurrentIssueAnswerHubLink; 91 | string SubscribeIssueJson = JsonConvert.SerializeObject(subscribeIssue, Formatting.Indented); 92 | File.WriteAllText(IssueFilesDirectory + IssueNumber + IssueTrackerFileExtension, SubscribeIssueJson); 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | public static void UnSubscribeFromIssue(string IssueNumber) 99 | { 100 | string FileLocation = IssueFilesDirectory + IssueNumber + IssueTrackerFileExtension; 101 | File.Delete(FileLocation); 102 | } 103 | 104 | public static SubscribeIssue GetSubscribedIssue(string IssueFilePath) 105 | { 106 | return JsonConvert.DeserializeObject(File.ReadAllText(IssueFilePath)); 107 | } 108 | 109 | public static string GetIssueNumber(string IssueLink) 110 | { 111 | return IssueLink.Replace(@"https://issues.unrealengine.com/issue/", ""); 112 | } 113 | 114 | public static string GetIssueFilesDirectory() 115 | { 116 | return IssueFilesDirectory; 117 | } 118 | 119 | public static string GetIssuesHistoryFile() 120 | { 121 | return IssueTrackerHistoryFile; 122 | } 123 | 124 | public static SolidColorBrush ToSolidColorBrush(string hex_code) 125 | { 126 | return (SolidColorBrush)new BrushConverter().ConvertFromString(hex_code); 127 | } 128 | 129 | public static IssueResolution GetResolution(string ResolutionName) 130 | { 131 | IssueResolution issueResolution = IssueResolution.None; 132 | switch (ResolutionName.ToLower()) 133 | { 134 | case "backlogged": 135 | issueResolution = IssueResolution.Backlogged; 136 | break; 137 | case "by design": 138 | issueResolution = IssueResolution.ByDesign; 139 | break; 140 | case "cannot reproduce": 141 | issueResolution = IssueResolution.CannotReproduce; 142 | break; 143 | case "duplicate": 144 | issueResolution = IssueResolution.Duplicate; 145 | break; 146 | case "fixed": 147 | issueResolution = IssueResolution.Fixed; 148 | break; 149 | case "non-issue": 150 | issueResolution = IssueResolution.NonIssue; 151 | break; 152 | case "unresolved": 153 | issueResolution = IssueResolution.Unresolved; 154 | break; 155 | case "won't do": 156 | issueResolution = IssueResolution.WontDo; 157 | break; 158 | case "won't fix": 159 | issueResolution = IssueResolution.WontFix; 160 | break; 161 | default: 162 | break; 163 | 164 | } 165 | return issueResolution; 166 | } 167 | 168 | public static SolidColorBrush GetResolutionColor(string ResolutionName) 169 | { 170 | return GetColorByIssueResolution(GetResolution(ResolutionName)); 171 | } 172 | 173 | private static SolidColorBrush GetColorByIssueResolution(IssueResolution issueResolution) 174 | { 175 | string hex_code = "#FF304FFE"; 176 | switch(issueResolution) 177 | { 178 | case IssueResolution.Backlogged: 179 | hex_code = "#FF7F261D"; 180 | break; 181 | case IssueResolution.ByDesign: 182 | hex_code = "#FF27AE60"; 183 | break; 184 | case IssueResolution.CannotReproduce: 185 | hex_code = "#FFF39C12"; 186 | break; 187 | case IssueResolution.Duplicate: 188 | hex_code = "#FF95A5A6"; 189 | break; 190 | case IssueResolution.Fixed: 191 | hex_code = "#FF2ECC71"; 192 | break; 193 | case IssueResolution.NonIssue: 194 | hex_code = "#FFBBBBBB"; 195 | break; 196 | case IssueResolution.Unresolved: 197 | hex_code = "#FFE74C3C"; 198 | break; 199 | case IssueResolution.WontDo: 200 | hex_code = "#FF171717"; 201 | break; 202 | case IssueResolution.WontFix: 203 | hex_code = "#FF34495E"; 204 | break; 205 | default: 206 | break; 207 | 208 | } 209 | return (SolidColorBrush)new BrushConverter().ConvertFromString(hex_code); 210 | } 211 | 212 | public static string RemoveHtmlTags(string html) 213 | { 214 | string ReturnValue = html.Replace(@"amp;", string.Empty); 215 | Regex.Replace(ReturnValue, "<.+?>", string.Empty); 216 | return ReturnValue; 217 | } 218 | } 219 | 220 | public class SubscribeIssue 221 | { 222 | public string SubscribedIssueID { get; set; } 223 | public string SubscribedIssueTitle { get; set; } 224 | public string SubscribedIssueResolution { get; set; } 225 | public string SubscribedIssueLink { get; set; } 226 | public string SubscribedIssueDescription { get; set; } 227 | public string SubscribedIssueRepro { get; set; } 228 | public string SubscribedIssueMoreInfo { get; set; } // AnswerHub or UDN link. 229 | } 230 | 231 | public class HistoryIssue 232 | { 233 | public Dictionary IssueHistory = new Dictionary(); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /UIssueTracker/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  17 | 18 | 19 | 20 | 21 | 24 | 27 | 30 | 31 | 34 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | # ISSUES FIXED 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 158 | 159 | 160 | 161 | 162 | 163 | HOLD ON...ALMOST THERE 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /UIssueTracker/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using CodeHollow.FeedReader; 2 | using System.Windows; 3 | using UIssueTracker.Classes; 4 | using UIssueTracker.UserControls; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using System.Windows.Threading; 8 | using System; 9 | 10 | namespace UIssueTracker 11 | { 12 | /// 13 | /// Interaction logic for MainWindow.xaml 14 | /// 15 | public partial class MainWindow : Window 16 | { 17 | public MainWindow() 18 | { 19 | InitializeComponent(); 20 | HistoryCategory.Visibility = Visibility.Collapsed; 21 | MyTransitioner.SelectedIndex = 0; 22 | Title = Constants.PRODUCT_NAME; 23 | 24 | Dispatcher.BeginInvoke((Action)LoadSubscribedIssuesAsync); 25 | LoadBugsAsync(); 26 | LoadFixesAsync(); 27 | LoadHistoryCards(); 28 | } 29 | 30 | public void LoadHistoryCards() 31 | { 32 | HistoryContainer.Children.Clear(); 33 | HistoryCategory.Visibility = Visibility.Collapsed; 34 | if (File.Exists(Constants.GetIssuesHistoryFile())) 35 | { 36 | HistoryIssue historyIssue = Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(Constants.GetIssuesHistoryFile())); 37 | foreach (var s in historyIssue.IssueHistory) 38 | { 39 | IssueHistoryCard issueHistoryCard = new IssueHistoryCard(s.Key, s.Value, this); 40 | HistoryContainer.Children.Add(issueHistoryCard); 41 | HistoryCategory.Visibility = Visibility.Visible; 42 | } 43 | } 44 | } 45 | 46 | public async void LoadSubscribedIssuesAsync() 47 | { 48 | await Task.Factory.StartNew(() => 49 | { 50 | Dispatcher.BeginInvoke(new Action(() => 51 | { 52 | if (Directory.Exists(Constants.GetIssueFilesDirectory())) 53 | { 54 | string[] SubscribedIssues = Directory.GetFiles(Constants.GetIssueFilesDirectory()); 55 | SubscribedIssuesContainer.Children.Clear(); 56 | int FixedIssues = 0; 57 | int TotalIssuesSubscribed = 0; 58 | foreach (string s in SubscribedIssues) 59 | { 60 | TotalIssuesSubscribed++; 61 | SubscribeIssue subscribeIssue = Constants.GetSubscribedIssue(s); 62 | IssueCard issueCard = new IssueCard(subscribeIssue.SubscribedIssueTitle, subscribeIssue.SubscribedIssueDescription, subscribeIssue.SubscribedIssueLink, this, null, true); 63 | if (issueCard.HasIssueResolutionChanged(s, out Constants.IssueResolution OutChangedResolution) == false) 64 | { 65 | if (OutChangedResolution != Constants.IssueResolution.Fixed) 66 | { 67 | SubscribedIssuesContainer.Children.Add(issueCard); 68 | } 69 | else 70 | { 71 | FixedIssues++; 72 | } 73 | } 74 | } 75 | if (FixedIssues > 0) 76 | { 77 | NumberOfIssuesFixed.Text = string.Format("{0}/{1} ISSUE(S) HAS BEEN FIXED!", FixedIssues, TotalIssuesSubscribed); 78 | NumberOfIssuesFixedDialogHost.IsOpen = true; 79 | } 80 | } 81 | })); 82 | }); 83 | } 84 | 85 | private async void LoadBugsAsync() 86 | { 87 | Feed feed = await FeedReader.ReadAsync(Constants.BUG_URL); 88 | foreach (var item in feed.Items) 89 | { 90 | BugsContainer.Children.Add(new IssueCard(item.Title, item.Description, item.Link, this, true)); 91 | } 92 | BugsLoadingBar.Visibility = Visibility.Collapsed; 93 | } 94 | 95 | private async void LoadFixesAsync() 96 | { 97 | Feed feed = await FeedReader.ReadAsync(Constants.FIX_URL); 98 | foreach (var item in feed.Items) 99 | { 100 | FixesContainer.Children.Add(new IssueCard(item.Title, item.Description, item.Link, this, false)); 101 | } 102 | FixesLoadingBar.Visibility = Visibility.Collapsed; 103 | } 104 | 105 | private void SearchIssue_Btn_Click(object sender, RoutedEventArgs e) 106 | { 107 | ShowTransitioner(@"https://issues.unrealengine.com/issue/" + IssueSearchText.Text, null, false); 108 | } 109 | 110 | public void ShowTransitioner(string Link, string Title, bool bIsSubscribedCard) 111 | { 112 | MyDialogHost.IsOpen = true; 113 | MyTransitioner.SelectedIndex = 1; 114 | IssueDetail.LoadIssue(Link, Title, bIsSubscribedCard, this); 115 | MyDialogHost.IsOpen = false; 116 | } 117 | 118 | private void UE4_Twitter_Click(object sender, RoutedEventArgs e) 119 | { 120 | System.Diagnostics.Process.Start(@"https://twitter.com/UnrealEngine"); 121 | } 122 | 123 | private void UE4_FB_Click(object sender, RoutedEventArgs e) 124 | { 125 | System.Diagnostics.Process.Start(@"https://www.facebook.com/UnrealEngine/"); 126 | } 127 | 128 | private void UE4_Git_Click(object sender, RoutedEventArgs e) 129 | { 130 | System.Diagnostics.Process.Start(@"https://github.com/EpicGames/UnrealEngine"); 131 | } 132 | 133 | private void Satheesh_Twitter_Click(object sender, RoutedEventArgs e) 134 | { 135 | System.Diagnostics.Process.Start(@"https://twitter.com/ryanjon2040"); 136 | } 137 | 138 | private void Satheesh_FB_Click(object sender, RoutedEventArgs e) 139 | { 140 | System.Diagnostics.Process.Start(@"https://www.facebook.com/satheeshpv"); 141 | } 142 | 143 | private void Satheesh_Git_Click(object sender, RoutedEventArgs e) 144 | { 145 | System.Diagnostics.Process.Start(@"https://github.com/ryanjon2040"); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /UIssueTracker/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Unreal Issue Tracker")] 11 | [assembly: AssemblyDescription("Desktop app designed to interact with Unreal Engine issues.")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Satheesh (ryanjon2040)")] 14 | [assembly: AssemblyProduct("UIssueTracker")] 15 | [assembly: AssemblyCopyright("Copyright © 2018 - Satheesh (ryanjon2040)")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /UIssueTracker/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UIssueTracker.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UIssueTracker.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /UIssueTracker/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /UIssueTracker/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UIssueTracker.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UIssueTracker/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /UIssueTracker/UIssueTracker.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D48705BF-338B-4495-AC43-683DA1A687CB} 8 | WinExe 9 | UIssueTracker 10 | UIssueTracker 11 | v4.6.1 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | uit_icon.ico 40 | 41 | 42 | 43 | ..\packages\CodeHollow.FeedReader.1.1.0.2\lib\net452\CodeHollow.FeedReader.dll 44 | 45 | 46 | ..\packages\HtmlAgilityPack.1.6.13\lib\Net45\HtmlAgilityPack.dll 47 | 48 | 49 | ..\packages\MaterialDesignColors.1.1.3\lib\net45\MaterialDesignColors.dll 50 | 51 | 52 | ..\packages\MaterialDesignThemes.2.3.1.953\lib\net45\MaterialDesignThemes.Wpf.dll 53 | 54 | 55 | ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 4.0 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | MSBuild:Compile 80 | Designer 81 | 82 | 83 | 84 | IssueCard.xaml 85 | 86 | 87 | IssueDetails.xaml 88 | 89 | 90 | IssueHistoryCard.xaml 91 | 92 | 93 | MSBuild:Compile 94 | Designer 95 | 96 | 97 | App.xaml 98 | Code 99 | 100 | 101 | MainWindow.xaml 102 | Code 103 | 104 | 105 | Designer 106 | MSBuild:Compile 107 | 108 | 109 | Designer 110 | MSBuild:Compile 111 | 112 | 113 | Designer 114 | MSBuild:Compile 115 | 116 | 117 | 118 | 119 | Code 120 | 121 | 122 | True 123 | True 124 | Resources.resx 125 | 126 | 127 | True 128 | Settings.settings 129 | True 130 | 131 | 132 | ResXFileCodeGenerator 133 | Resources.Designer.cs 134 | 135 | 136 | 137 | SettingsSingleFileGenerator 138 | Settings.Designer.cs 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueCard.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueCard.xaml.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Controls; 5 | using UIssueTracker.Classes; 6 | 7 | namespace UIssueTracker.UserControls 8 | { 9 | /// 10 | /// Interaction logic for IssueCard.xaml 11 | /// 12 | public partial class IssueCard : UserControl 13 | { 14 | private MainWindow mainWindow = null; 15 | private bool _bIsSubscribedCard = false; 16 | private string LoadURL = null; 17 | 18 | public IssueCard(string Title, string Description, string Link, MainWindow NewMainWindow, bool? bIsBug = null, bool bIsSubscribedCard = false) 19 | { 20 | InitializeComponent(); 21 | mainWindow = NewMainWindow; 22 | CardTitle.Header = Title; 23 | CardDescription.Text = Constants.RemoveHtmlTags(Description); 24 | CardLink.Text = Link; 25 | LoadURL = Link; 26 | _bIsSubscribedCard = bIsSubscribedCard; 27 | if (bIsBug != null) 28 | { 29 | CardTitle.Foreground = (bool)bIsBug ? Constants.ToSolidColorBrush("#DDE21717") : Constants.ToSolidColorBrush("#DD4EE217"); 30 | } 31 | } 32 | 33 | public bool HasIssueResolutionChanged(string FilePath, out Constants.IssueResolution ChangedResolution) 34 | { 35 | HtmlWeb web = new HtmlWeb(); 36 | HtmlDocument htmlDoc = web.Load(LoadURL); 37 | 38 | string CurrentIssueStatus = htmlDoc.DocumentNode.SelectNodes("//*[@class='resolution']").Select(n => n.InnerText.Trim()).ElementAt(0); 39 | CardTitle.Foreground = Constants.GetResolutionColor(CurrentIssueStatus); 40 | if (Constants.GetSubscribedIssue(FilePath).SubscribedIssueResolution.ToLower() != CurrentIssueStatus.ToLower()) 41 | { 42 | ChangedResolution = Constants.GetResolution(CurrentIssueStatus); 43 | return true; 44 | } 45 | 46 | ChangedResolution = Constants.IssueResolution.None; 47 | return false; 48 | } 49 | 50 | private void ShowDetails_Btn_Click(object sender, System.Windows.RoutedEventArgs e) 51 | { 52 | mainWindow.ShowTransitioner(CardLink.Text, CardTitle.Header.ToString(), _bIsSubscribedCard); 53 | } 54 | 55 | private void IssueLink_Click(object sender, System.Windows.RoutedEventArgs e) 56 | { 57 | System.Diagnostics.Process.Start(LoadURL); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueDetails.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | DETAILS 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Unresolved 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueDetails.xaml.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using System; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using UIssueTracker.Classes; 7 | 8 | namespace UIssueTracker.UserControls 9 | { 10 | /// 11 | /// Interaction logic for IssueDetails.xaml 12 | /// 13 | public partial class IssueDetails : UserControl 14 | { 15 | public string IssueURL { get; private set; } 16 | public string CurrentIssueTitle { get; private set; } 17 | public string CurrentIssueStatus { get; private set; } 18 | public string CurrentIssueDescription { get; private set; } 19 | public string CurrentIssueRepro { get; private set; } 20 | public string CurrentIssueAnswerHubLink { get; private set; } 21 | 22 | private bool bDescriptionSet = false; 23 | private bool bStepsToReproduceSet = false; 24 | private string CurrentIssueNumber = null; 25 | private MainWindow OwningWindow = null; 26 | 27 | public IssueDetails() 28 | { 29 | InitializeComponent(); 30 | } 31 | 32 | public void LoadIssue(string URL, string Title, bool bIsSubscribedCard, MainWindow mainWindow) 33 | { 34 | OwningWindow = mainWindow; 35 | CurrentIssueAnswerHubLink = null; 36 | 37 | bDescriptionSet = false; 38 | bStepsToReproduceSet = false; 39 | IssueURL = URL; 40 | 41 | HtmlWeb web = new HtmlWeb(); 42 | HtmlDocument htmlDoc = web.Load(IssueURL); 43 | 44 | CurrentIssueNumber = Constants.GetIssueNumber(URL); 45 | 46 | if (Title == null) 47 | { 48 | Title = htmlDoc.DocumentNode.SelectNodes("//*[@class='visible-xs']").Select(n => n.InnerText.Trim()).ElementAt(0); 49 | } 50 | CurrentIssueTitle = Title; 51 | IssueTitle.Text = CurrentIssueTitle; 52 | CurrentIssueStatus = htmlDoc.DocumentNode.SelectNodes("//*[@class='resolution']").Select(n => n.InnerText.Trim()).ElementAt(0); 53 | IssueStatus.Text = CurrentIssueStatus; 54 | 55 | Subscribe_Btn.IsEnabled = true; 56 | if (bIsSubscribedCard == false) 57 | { 58 | Subscribe_Btn.IsEnabled = CurrentIssueStatus != "Fixed"; 59 | } 60 | Subscribe_Btn.Content = Constants.IsSubscribedToIssue(CurrentIssueNumber) ? "UNSUBSCRIBE" : "SUBSCRIBE"; 61 | 62 | IssueStatusColor.Background = Constants.GetResolutionColor(IssueStatus.Text); 63 | 64 | HtmlNode[] aNodes = htmlDoc.DocumentNode.SelectNodes("//a").ToArray(); 65 | foreach (var n in aNodes) 66 | { 67 | string InnerText = n.InnerText.ToLower(); 68 | if (n.Attributes["href"] != null 69 | && (InnerText == "answerhub" || InnerText == "udn") 70 | && CurrentIssueAnswerHubLink == null 71 | && (n.Attributes["href"].Value.EndsWith("ask.html") == false || n.Attributes["href"].Value.Contains("udn.unrealengine.com"))) 72 | { 73 | CurrentIssueAnswerHubLink = n.Attributes["href"].Value; 74 | AnswerHubLinkBtn.Content = "VISIT ANSWER HUB"; 75 | if (InnerText == "udn") 76 | { 77 | AnswerHubLinkBtn.Content = "VISIT UDN"; 78 | } 79 | break; 80 | } 81 | } 82 | AnswerHubLinkBtn.IsEnabled = CurrentIssueAnswerHubLink != null; 83 | 84 | SideStackPanel.Children.Clear(); 85 | HtmlNodeCollection thcells = htmlDoc.DocumentNode.SelectNodes("//table[@class='table']/tr/th"); 86 | for (int i = 0; i < thcells.Count; ++i) 87 | { 88 | HtmlNodeCollection cells = htmlDoc.DocumentNode.SelectNodes("//table[@class='table']/tr/td"); 89 | var DynamicPanelButton = new Button() { Content = Constants.RemoveHtmlTags(cells[i].InnerText) }; 90 | DynamicPanelButton.IsEnabled = false; 91 | HtmlNode a = cells[i].SelectSingleNode("a[@href]"); 92 | if (a != null) 93 | { 94 | DynamicPanelButton.Tag = Constants.RemoveHtmlTags(a.Attributes["href"].Value); 95 | DynamicPanelButton.Click += DynamicPanelButton_Click; 96 | DynamicPanelButton.IsEnabled = true; 97 | } 98 | SideStackPanel.Children.Add(DynamicPanelButton); 99 | } 100 | 101 | var texts = htmlDoc.DocumentNode.SelectNodes("//*[@class='panel-body']").Select(n => n.InnerText.Trim()); 102 | foreach (var text in texts) 103 | { 104 | if (bDescriptionSet == false) 105 | { 106 | bDescriptionSet = true; 107 | CurrentIssueDescription = text; 108 | IssueDescription.Text = CurrentIssueDescription; 109 | continue; 110 | } 111 | 112 | if (bStepsToReproduceSet == false) 113 | { 114 | bStepsToReproduceSet = true; 115 | CurrentIssueRepro = text; 116 | IssueReproduceSteps.Text = CurrentIssueRepro; 117 | break; 118 | } 119 | } 120 | 121 | Constants.SaveToHistory(this); 122 | OwningWindow.LoadHistoryCards(); 123 | } 124 | 125 | private void DynamicPanelButton_Click(object sender, RoutedEventArgs e) //Event which will be triggered on click of DynamicPanelButton 126 | { 127 | string Tag = ((Button)sender).Tag.ToString(); 128 | Console.WriteLine("TAG: " + Tag); 129 | if (Tag.StartsWith(@"/issue/")) 130 | { 131 | System.Diagnostics.Process.Start(@"https://issues.unrealengine.com" + Tag); 132 | } 133 | else 134 | { 135 | System.Diagnostics.Process.Start(Tag); 136 | } 137 | } 138 | 139 | private void Subscribe_Btn_Click(object sender, RoutedEventArgs e) 140 | { 141 | if ((string)Subscribe_Btn.Content == "UNSUBSCRIBE") 142 | { 143 | Constants.UnSubscribeFromIssue(CurrentIssueNumber); 144 | Subscribe_Btn.Content = "SUBSCRIBE"; 145 | } 146 | else 147 | { 148 | if (Constants.SubscribeToIssue(this)) 149 | { 150 | Subscribe_Btn.Content = "UNSUBSCRIBE"; 151 | } 152 | } 153 | OwningWindow.LoadSubscribedIssuesAsync(); 154 | } 155 | 156 | private void AnswerHubLinkBtn_Click(object sender, RoutedEventArgs e) 157 | { 158 | System.Diagnostics.Process.Start(CurrentIssueAnswerHubLink); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueHistoryCard.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | ISSUE NUMBER 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UIssueTracker/UserControls/IssueHistoryCard.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Windows.Controls; 3 | using UIssueTracker.Classes; 4 | 5 | namespace UIssueTracker.UserControls 6 | { 7 | /// 8 | /// Interaction logic for IssueHistoryCard.xaml 9 | /// 10 | public partial class IssueHistoryCard : UserControl 11 | { 12 | private string IssueKey = null; 13 | private MainWindow OwningWindow = null; 14 | public IssueHistoryCard(string NewIssueNumber, string NewIssueTitle, MainWindow mainWindow) 15 | { 16 | InitializeComponent(); 17 | CardTitle.Text = string.Format("({0}) {1}", NewIssueNumber, NewIssueTitle); 18 | IssueKey = NewIssueNumber; 19 | OwningWindow = mainWindow; 20 | } 21 | 22 | private void RemoveFromHistory_Btn_Click(object sender, System.Windows.RoutedEventArgs e) 23 | { 24 | HistoryIssue historyIssue = Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(Constants.GetIssuesHistoryFile())); 25 | historyIssue.IssueHistory.Remove(IssueKey); 26 | string Result = Newtonsoft.Json.JsonConvert.SerializeObject(historyIssue, Newtonsoft.Json.Formatting.Indented); 27 | File.WriteAllText(Constants.GetIssuesHistoryFile(), Result); 28 | OwningWindow.LoadHistoryCards(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UIssueTracker/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UIssueTracker/uit_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryanjon2040/Unreal-Issue-Tracker/ddc2dd16547985cfa6679afa5f826f253a8183e1/UIssueTracker/uit_icon.ico -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile --------------------------------------------------------------------------------