From 8bcada8af24fa17fb594d3395beb5bc0158cf5d9 Mon Sep 17 00:00:00 2001 From: Spruce Weber-Milne Date: Fri, 19 Nov 2021 14:27:17 -0800 Subject: [PATCH 1/2] Port app to Blazor --- .vscode/launch.json | 35 +++ .vscode/tasks.json | 42 ++++ FeedbackBot.sln | 32 +-- src/FeedbackBot/App.razor | 14 ++ src/FeedbackBot/AppSettings.cs | 6 +- .../Controllers/AccountController.cs | 39 ---- src/FeedbackBot/Controllers/HomeController.cs | 203 ------------------ src/FeedbackBot/FeedbackBot.csproj | 16 +- src/FeedbackBot/Models/AuthSettings.cs | 2 +- src/FeedbackBot/Models/CommentContainer.cs | 6 +- src/FeedbackBot/Models/IssueContainer.cs | 18 +- .../Models/IssueDetailsViewModel.cs | 4 +- src/FeedbackBot/Pages/AppIssues.razor | 46 ++++ src/FeedbackBot/Pages/Counter.razor | 18 ++ src/FeedbackBot/Pages/Details.razor | 180 ++++++++++++++++ src/FeedbackBot/Pages/Error.cshtml | 42 ++++ src/FeedbackBot/Pages/Error.cshtml.cs | 26 +++ src/FeedbackBot/Pages/Index.razor | 69 ++++++ src/FeedbackBot/Pages/_Host.cshtml | 8 + src/FeedbackBot/Pages/_Layout.cshtml | 48 +++++ src/FeedbackBot/Program.cs | 84 ++++++-- .../Properties/launchSettings.json | 26 +-- src/FeedbackBot/Services/GitHubService.cs | 115 +++++----- .../Services/GitHubServiceExtensions.cs | 27 +++ src/FeedbackBot/Services/UserService.cs | 28 +++ src/FeedbackBot/Shared/CreateIssue.razor | 60 ++++++ src/FeedbackBot/Shared/DisplayIssues.razor | 72 +++++++ src/FeedbackBot/Shared/MainLayout.razor | 27 +++ src/FeedbackBot/Shared/SearchBar.razor | 48 +++++ src/FeedbackBot/Shared/VoteButton.razor | 63 ++++++ src/FeedbackBot/Startup.cs | 92 -------- .../Utilities/BulkObservableCollection.cs | 39 ++++ src/FeedbackBot/Views/Home/Details.cshtml | 84 -------- .../Views/Home/DisplayIssuesPartial.cshtml | 48 ----- src/FeedbackBot/Views/Home/Index.cshtml | 64 ------ .../Views/Home/NewIssuePartial.cshtml | 18 -- src/FeedbackBot/Views/Home/Search.cshtml | 21 -- .../Views/Home/SearchBarPartial.cshtml | 10 - src/FeedbackBot/Views/Home/app.cshtml | 22 -- src/FeedbackBot/Views/Shared/Error.cshtml | 14 -- src/FeedbackBot/Views/Shared/_Layout.cshtml | 63 ------ .../Views/Shared/css_browser_selector.js | 0 src/FeedbackBot/Views/_ViewImports.cshtml | 2 - src/FeedbackBot/Views/_ViewStart.cshtml | 3 - src/FeedbackBot/_Imports.razor | 18 ++ src/FeedbackBot/appsettings.Development.json | 6 +- src/FeedbackBot/appsettings.json | 13 +- src/FeedbackBot/wwwroot/_references.js | 7 - src/FeedbackBot/wwwroot/css/site.css | 29 +++ src/FeedbackBot/wwwroot/css/site.min.css | 29 +++ src/FeedbackBot/wwwroot/favicon.ico | Bin 32038 -> 5430 bytes src/FeedbackBot/wwwroot/js/site.js | 18 -- 52 files changed, 1153 insertions(+), 851 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 src/FeedbackBot/App.razor delete mode 100644 src/FeedbackBot/Controllers/AccountController.cs delete mode 100644 src/FeedbackBot/Controllers/HomeController.cs create mode 100644 src/FeedbackBot/Pages/AppIssues.razor create mode 100644 src/FeedbackBot/Pages/Counter.razor create mode 100644 src/FeedbackBot/Pages/Details.razor create mode 100644 src/FeedbackBot/Pages/Error.cshtml create mode 100644 src/FeedbackBot/Pages/Error.cshtml.cs create mode 100644 src/FeedbackBot/Pages/Index.razor create mode 100644 src/FeedbackBot/Pages/_Host.cshtml create mode 100644 src/FeedbackBot/Pages/_Layout.cshtml create mode 100644 src/FeedbackBot/Services/GitHubServiceExtensions.cs create mode 100644 src/FeedbackBot/Services/UserService.cs create mode 100644 src/FeedbackBot/Shared/CreateIssue.razor create mode 100644 src/FeedbackBot/Shared/DisplayIssues.razor create mode 100644 src/FeedbackBot/Shared/MainLayout.razor create mode 100644 src/FeedbackBot/Shared/SearchBar.razor create mode 100644 src/FeedbackBot/Shared/VoteButton.razor delete mode 100644 src/FeedbackBot/Startup.cs create mode 100644 src/FeedbackBot/Utilities/BulkObservableCollection.cs delete mode 100644 src/FeedbackBot/Views/Home/Details.cshtml delete mode 100644 src/FeedbackBot/Views/Home/DisplayIssuesPartial.cshtml delete mode 100644 src/FeedbackBot/Views/Home/Index.cshtml delete mode 100644 src/FeedbackBot/Views/Home/NewIssuePartial.cshtml delete mode 100644 src/FeedbackBot/Views/Home/Search.cshtml delete mode 100644 src/FeedbackBot/Views/Home/SearchBarPartial.cshtml delete mode 100644 src/FeedbackBot/Views/Home/app.cshtml delete mode 100644 src/FeedbackBot/Views/Shared/Error.cshtml delete mode 100644 src/FeedbackBot/Views/Shared/_Layout.cshtml delete mode 100644 src/FeedbackBot/Views/Shared/css_browser_selector.js delete mode 100644 src/FeedbackBot/Views/_ViewImports.cshtml delete mode 100644 src/FeedbackBot/Views/_ViewStart.cshtml create mode 100644 src/FeedbackBot/_Imports.razor delete mode 100644 src/FeedbackBot/wwwroot/_references.js delete mode 100644 src/FeedbackBot/wwwroot/js/site.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1a35261 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/FeedbackBot/bin/Debug/net6.0/FeedbackBot.dll", + "args": [], + "cwd": "${workspaceFolder}/src/FeedbackBot", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..970a405 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/FeedbackBot/FeedbackBot.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/src/FeedbackBot/FeedbackBot.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/src/FeedbackBot/FeedbackBot.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/FeedbackBot.sln b/FeedbackBot.sln index 037bcb4..345f629 100644 --- a/FeedbackBot.sln +++ b/FeedbackBot.sln @@ -1,39 +1,27 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{93CEE17B-350D-4310-8CF2-85F5CEF0EF6C}" +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3B9EDB84-68AC-4FC1-B020-F7F5AF5F3AFA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeedbackBot", "src\FeedbackBot\FeedbackBot.csproj", "{A17571A3-DF4C-4994-85FB-E8DA148EDA4A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeedbackBot", "src\FeedbackBot\FeedbackBot.csproj", "{B232FF92-9D2F-45E7-91AD-3824A59F3F6C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|x64.ActiveCfg = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|x64.Build.0 = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|x86.ActiveCfg = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Debug|x86.Build.0 = Debug|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|Any CPU.Build.0 = Release|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|x64.ActiveCfg = Release|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|x64.Build.0 = Release|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|x86.ActiveCfg = Release|Any CPU - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A}.Release|x86.Build.0 = Release|Any CPU + {B232FF92-9D2F-45E7-91AD-3824A59F3F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B232FF92-9D2F-45E7-91AD-3824A59F3F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B232FF92-9D2F-45E7-91AD-3824A59F3F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B232FF92-9D2F-45E7-91AD-3824A59F3F6C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A17571A3-DF4C-4994-85FB-E8DA148EDA4A} = {93CEE17B-350D-4310-8CF2-85F5CEF0EF6C} + {B232FF92-9D2F-45E7-91AD-3824A59F3F6C} = {3B9EDB84-68AC-4FC1-B020-F7F5AF5F3AFA} EndGlobalSection EndGlobal diff --git a/src/FeedbackBot/App.razor b/src/FeedbackBot/App.razor new file mode 100644 index 0000000..03cc869 --- /dev/null +++ b/src/FeedbackBot/App.razor @@ -0,0 +1,14 @@ + + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
diff --git a/src/FeedbackBot/AppSettings.cs b/src/FeedbackBot/AppSettings.cs index 530f962..a6c030b 100644 --- a/src/FeedbackBot/AppSettings.cs +++ b/src/FeedbackBot/AppSettings.cs @@ -2,8 +2,8 @@ namespace FeedbackBot { public class AppSettings { - public string GitHubUser { get; set; } - public string GitHubPassword { get; set; } - public string GitHubToken { get; set; } + public string GitHubUser { get; set; } = ""; + public string GitHubPassword { get; set; } = ""; + public string GitHubToken { get; set; } = ""; } } \ No newline at end of file diff --git a/src/FeedbackBot/Controllers/AccountController.cs b/src/FeedbackBot/Controllers/AccountController.cs deleted file mode 100644 index ffdd3c4..0000000 --- a/src/FeedbackBot/Controllers/AccountController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Threading.Tasks; -using AspNetCore.Security.CAS; -using FeedbackBot.Models; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; - -namespace FeedbackBot.Controllers -{ - public class AccountController : Controller - { - private readonly AuthSettings _authSettings; - public AccountController(IOptions authSettings) - { - _authSettings = authSettings.Value; - } - public IActionResult AccessDenied() - { - return View(); - } - - [AllowAnonymous] - [Route("login")] - public async Task Login(string returnUrl) - { - var props = new AuthenticationProperties { RedirectUri = returnUrl }; - await HttpContext.ChallengeAsync(CasDefaults.AuthenticationScheme, props); - } - - [Route("logout")] - public async Task Logout() - { - await HttpContext.SignOutAsync(); - return Redirect($"{_authSettings.CasBaseUrl}logout"); - } - - } -} diff --git a/src/FeedbackBot/Controllers/HomeController.cs b/src/FeedbackBot/Controllers/HomeController.cs deleted file mode 100644 index 6aedb51..0000000 --- a/src/FeedbackBot/Controllers/HomeController.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using FeedbackBot.Models; -using FeedbackBot.Services; -using Microsoft.AspNetCore.Authorization; -using Octokit; - -namespace FeedbackBot.Controllers -{ - [Authorize] - public class HomeController : Controller - { - private readonly IGitHubService _gitHubService; - - public HomeController(IGitHubService gitHubService) - { - _gitHubService = gitHubService; - } - - public IActionResult Index() - { - ViewData["Message"] = "Welcome"; - - return View(); - } - - [HttpGet("/app/{appName}")] - public async Task App(string appName) - { - var issues = await _gitHubService.GetIssues(appName); - - // List out all the current issues - var issueContainerList = new List(); - foreach (var i in issues) - { - var newIssueContainer = new IssueContainer { Kerberos = User.Identity.Name }; - newIssueContainer.Deserialize(i); - issueContainerList.Add(newIssueContainer); - } - ViewData["AppName"] = appName; - ViewData["Message"] = "Current Feedback for " + appName; - - return View(issueContainerList); - } - - /* - * Creates a new issue in FeedbackBot repository - * @param string title - The title of the feedback [required] - * @param string descrption - The description of the feedback - */ - [HttpPost("createIssue")] - public async Task CreateIssue(string title, string description, string appName) - { - // Initialize new issue - var createIssue = new NewIssue(title) - { - Body = - $"{description}\r\n" + - "--------------------\r\n" + - "Votes: 1\r\n" + - $"Author: {User.Identity.Name}\r\n" + - $"Voters: {User.Identity.Name}", - Labels = { "feedback" } - }; - var issue = await _gitHubService.CreateIssue(appName, createIssue); - return RedirectToAction("App", "home", new { appName }); - } - - [HttpPost("search")] - public async Task Search(string searchInput, string appName) - { - if (searchInput == null) - { - return RedirectToAction("App", "home", new { appName }); - } - - var results = await _gitHubService.Search(appName, searchInput); - - var issueContainerList = new List(); - foreach (var i in results.Items) - { - var newIssueContainer = new IssueContainer { Kerberos = User.Identity.Name }; - newIssueContainer.Deserialize(i); - issueContainerList.Add(newIssueContainer); - } - ViewData["AppName"] = appName; - ViewData["SearchTerm"] = searchInput; - ViewData["Message"] = "Search Results For " + searchInput + " In " + appName; - - return View(issueContainerList); - } - - [HttpPost("addComment")] - public async Task AddComment(string comment, string voteID, string appName) - { - var id = int.Parse(voteID); - var body = $"{comment} \r\n" + - "--------------------\r\n" + - $"Author: {User.Identity.Name}"; - var issue = await _gitHubService.GetIssue(appName, id); - var result = await _gitHubService.AddComment(appName, id, body); - - return RedirectToAction("details", "home", new { appName = appName, id = voteID }); - } - - /* - * Upvotes a feedback issue - * @param string voteID - The number of the issue we are upvoting - */ - [HttpPost("vote")] - public async Task Vote(string voteID, string appName) - { - var id = int.Parse(voteID); - - var issue = await _gitHubService.GetIssue(appName, id); - var newIssueContainer = new IssueContainer { Kerberos = User.Identity.Name }; - - newIssueContainer.Deserialize(issue); - - if (newIssueContainer.VoteState == "unvote") - { - var newVoteCount = newIssueContainer.NumOfVotesInt - 1; - newIssueContainer.NumOfVotesInt = newVoteCount; - newIssueContainer.NumOfVotes = newVoteCount.ToString(); - newIssueContainer.ListOfVoters.Remove(User.Identity.Name); - } - else - { - // Update vote count - var newVoteCount = newIssueContainer.NumOfVotesInt + 1; - newIssueContainer.NumOfVotesInt = newVoteCount; - newIssueContainer.NumOfVotes = newVoteCount.ToString(); - newIssueContainer.ListOfVoters.Add(User.Identity.Name); - } - newIssueContainer.StringOfVoters = string.Join(",", newIssueContainer.ListOfVoters.ToArray()).Trim().TrimStart(','); - - // Update issue on GitHub - var update = issue.ToUpdate(); - update.Body = newIssueContainer.Serialize(); - await _gitHubService.UpdateIssue(appName, id, update); - - TempData["voteValidationMessage"] = "Thank you for voting on this issue!"; - - // Update voters - return RedirectToAction("details", "home", new { appName = appName, id = voteID }); - } - - /* - * Displays the details of an issue along with all its comments - * @param string voteID - The number of the issue we are upvoting - */ - [HttpGet("/issues/{appName}/{id}")] - public async Task Details(string appName, int id) - { - // Getting issue - var issue = await _gitHubService.GetIssue(appName, id); - var newIssueContainer = new IssueContainer { Kerberos = User.Identity.Name }; - newIssueContainer.Deserialize(issue); - - // Getting all comments in the issue - var issueComments = await _gitHubService.GetComments(appName, id); - var listOfComments = new List(); - - foreach (var i in issueComments) { - - var indexOfLine = i.Body.IndexOf ("--------------------"); - // add to the list if the issue is from FeedbackBot. - if (indexOfLine >= 0) { - - var bodycomment = i.Body.Substring (0, indexOfLine); - // don't add to the list if its an issue with empty comment. - if (!String.IsNullOrWhiteSpace (bodycomment)) { - var commentContainer = new CommentContainer (); - commentContainer.Deserialize (i, bodycomment); - listOfComments.Add (commentContainer); - } - } - } - - // Update model view - var issuesView = new IssueDetailsViewModel - { - Comments = listOfComments, - Issue = newIssueContainer - }; - - if (TempData["voteValidationMessage"] != null) - { - issuesView.VoteMessage = TempData["voteValidationMessage"].ToString(); - } - ViewData["AppName"] = appName; - - return View(issuesView); - } - - public IActionResult Error() - { - return View(); - } - } -} diff --git a/src/FeedbackBot/FeedbackBot.csproj b/src/FeedbackBot/FeedbackBot.csproj index 13762ee..f4e6526 100644 --- a/src/FeedbackBot/FeedbackBot.csproj +++ b/src/FeedbackBot/FeedbackBot.csproj @@ -1,15 +1,19 @@ - netcoreapp3.1 - InProcess - 3d018503-0709-4725-a85f-46666fed579c + net6.0 + enable + enable + aspnet-FeedbackBot-FD20D021-FD9A-4865-B014-0002FD473E09 + 0 - - - + + + + + diff --git a/src/FeedbackBot/Models/AuthSettings.cs b/src/FeedbackBot/Models/AuthSettings.cs index e03a23f..f77af8a 100644 --- a/src/FeedbackBot/Models/AuthSettings.cs +++ b/src/FeedbackBot/Models/AuthSettings.cs @@ -7,6 +7,6 @@ namespace FeedbackBot.Models { public class AuthSettings { - public string CasBaseUrl { get; set; } + public string? CasBaseUrl { get; set; } } } diff --git a/src/FeedbackBot/Models/CommentContainer.cs b/src/FeedbackBot/Models/CommentContainer.cs index 86f280b..2f744a7 100644 --- a/src/FeedbackBot/Models/CommentContainer.cs +++ b/src/FeedbackBot/Models/CommentContainer.cs @@ -5,11 +5,11 @@ namespace FeedbackBot.Models { public class CommentContainer { - public string Body { get; set; } + public string? Body { get; set; } - public string Author { get; set; } + public string? Author { get; set; } - public string CreateDate { get; set; } + public string? CreateDate { get; set; } public void Deserialize(IssueComment comment, string bodyComment) { diff --git a/src/FeedbackBot/Models/IssueContainer.cs b/src/FeedbackBot/Models/IssueContainer.cs index 327f1d5..4b6f8d6 100644 --- a/src/FeedbackBot/Models/IssueContainer.cs +++ b/src/FeedbackBot/Models/IssueContainer.cs @@ -8,25 +8,25 @@ namespace FeedbackBot.Models { public class IssueContainer { - public string Title { get; set; } + public string? Title { get; set; } - public string NumOfVotes { get; set; } + public string? NumOfVotes { get; set; } public int NumOfVotesInt { get; set; } - public string StringOfVoters { get; set; } + public string? StringOfVoters { get; set; } - public List ListOfVoters { get; set; } + public List ListOfVoters { get; set; } = new (); - public string Body { get; set; } + public string? Body { get; set; } public int Number { get; set; } - public string VoteState { get; set; } + public string? VoteState { get; set; } - public string Kerberos { get; set; } + public string? Kerberos { get; set; } - public string Author { get; set; } + public string? Author { get; set; } public int NumOfComments { get; set; } @@ -70,7 +70,7 @@ public void Deserialize(Issue issue) StringOfVoters = issueBody.Substring(indexOfVoters + 7); ListOfVoters = StringOfVoters.Split(',').Select(d => d.Trim()).ToList(); - VoteState = StringOfVoters.IndexOf(Kerberos, StringComparison.Ordinal) > 0 ? "unvote" : "vote"; + VoteState = StringOfVoters.IndexOf(Kerberos ?? "", StringComparison.Ordinal) > 0 ? "unvote" : "vote"; } } } diff --git a/src/FeedbackBot/Models/IssueDetailsViewModel.cs b/src/FeedbackBot/Models/IssueDetailsViewModel.cs index 743b512..3f2455d 100644 --- a/src/FeedbackBot/Models/IssueDetailsViewModel.cs +++ b/src/FeedbackBot/Models/IssueDetailsViewModel.cs @@ -10,10 +10,10 @@ public IssueDetailsViewModel() Comments = new List(); } - public IssueContainer Issue { get; set; } + public IssueContainer? Issue { get; set; } public List Comments { get; set; } - public string VoteMessage { get; set; } + public string? VoteMessage { get; set; } } } diff --git a/src/FeedbackBot/Pages/AppIssues.razor b/src/FeedbackBot/Pages/AppIssues.razor new file mode 100644 index 0000000..4f13cdf --- /dev/null +++ b/src/FeedbackBot/Pages/AppIssues.razor @@ -0,0 +1,46 @@ +@page "/app/{appName}" +@inject IGitHubService gitHubService +@inject IUserService userService + +Home Page - FeedbackBot + +
+ +

@appName

+ + + + + + +
+ +
+ +@code { + [Parameter] + public string? appName { get; set; } + + private BulkObservableCollection appIssues { get; init; } = new(); + + protected override async Task OnInitializedAsync() + { + if (!await userService.IsAuthenticated() || string.IsNullOrEmpty(appName)) + { + return; + } + + var kerberos = await userService.GetKerberos(); + var issues = await gitHubService.GetIssues(appName); + + // List out all the current issues + appIssues.Clear(); + appIssues.AddRange(issues.Select(i => + { + var newIssueContainer = new IssueContainer { Kerberos = kerberos }; + newIssueContainer.Deserialize(i); + return newIssueContainer; + })); + + } +} \ No newline at end of file diff --git a/src/FeedbackBot/Pages/Counter.razor b/src/FeedbackBot/Pages/Counter.razor new file mode 100644 index 0000000..cdd866f --- /dev/null +++ b/src/FeedbackBot/Pages/Counter.razor @@ -0,0 +1,18 @@ +@page "/counter" + +Counter + +

Counter

+ +

Current count: @currentCount

+ + + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/FeedbackBot/Pages/Details.razor b/src/FeedbackBot/Pages/Details.razor new file mode 100644 index 0000000..d4b1d9b --- /dev/null +++ b/src/FeedbackBot/Pages/Details.razor @@ -0,0 +1,180 @@ +@page "/issues/{appName}/{id:int}" +@inject IGitHubService gitHubService +@inject IUserService userService + +Issue - FeedbackBot + +

@message

+ + + + @if (issueDetails != null) + { + @if (issueDetails.VoteMessage != null) + { + + } +
+ +
+ Back +
+ +
+
+
+
+

@issueDetails.Issue?.Title

+ +
+

by @issueDetails.Issue?.Author

+
+ +
+
+ @issueDetails.Issue?.Body
+
+ +
+

+ Upvoted by: + @issueDetails.Issue?.StringOfVoters +

+
+ +
+
+
+
+
+
+

Submit New Comment


+
+ + +
+ + + +
+
+
+ +
+ @foreach (var comment in issueDetails.Comments) + { +
+
+
+
Date Created: @comment.CreateDate

+
Comment: @comment.Body

+
Author: @comment.Author


+
+
+
+ } +
+
+ } +
+ +@code { + + [Parameter] + public string? appName { get; set; } + + [Parameter] + public int id { get; set; } + + private IssueDetailsViewModel? issueDetails { get; set; } + + private string message { get; set; } = ""; + + private FormModel formModel = new(); + + + protected override async Task OnInitializedAsync() + { + await FetchIssueDetails(); + } + + private async Task FetchIssueDetails() + { + if (!await userService.IsAuthenticated() || string.IsNullOrEmpty(appName)) + { + return; + } + + var kerberos = await userService.GetKerberos(); + + // Getting issue + var issue = await gitHubService.GetIssue(appName, id); + var newIssueContainer = new IssueContainer { Kerberos = kerberos }; + newIssueContainer.Deserialize(issue); + + // Getting all comments in the issue + var issueComments = await gitHubService.GetComments(appName, id); + if (issueComments == null) + { + return; + } + + var listOfComments = issueComments.Select(c => c.ToIssueCommentContainer()) + .Where(c => c != null).Cast().ToList(); + + issueDetails = new IssueDetailsViewModel + { + Comments = listOfComments, + Issue = newIssueContainer + }; + } + + private void Voted(IssueContainer issue) + { + if (issueDetails != null) + { + issueDetails.Issue = issue; + issueDetails.VoteMessage = "Thank you for voting on this issue!"; + StateHasChanged(); + } + } + + private async Task SubmitComment() + { + if (!await userService.IsAuthenticated() || string.IsNullOrEmpty(appName) || issueDetails == null) + { + return; + } + + var kerberos = await userService.GetKerberos(); + + var body = $"{formModel.Comment} \r\n" + + "--------------------\r\n" + + $"Author: {kerberos}"; + + var issue = await gitHubService.GetIssue(appName, id); + var result = await gitHubService.AddComment(appName, id, body); + + if (result == null) + { + return; + } + + var comment = result.ToIssueCommentContainer(); + + if (comment == null) + { + return; + } + + issueDetails.Comments.Insert(0, comment); + } + + + private class FormModel + { + [Required] + public string Comment { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/FeedbackBot/Pages/Error.cshtml b/src/FeedbackBot/Pages/Error.cshtml new file mode 100644 index 0000000..19cb643 --- /dev/null +++ b/src/FeedbackBot/Pages/Error.cshtml @@ -0,0 +1,42 @@ +@page +@model FeedbackBot.Pages.ErrorModel + + + + + + + + Error + + + + + +
+
+

Error.

+

An error occurred while processing your request.

+ + @if (Model.ShowRequestId) + { +

+ Request ID: @Model.RequestId +

+ } + +

Development Mode

+

+ Swapping to the Development environment displays detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+
+
+ + + diff --git a/src/FeedbackBot/Pages/Error.cshtml.cs b/src/FeedbackBot/Pages/Error.cshtml.cs new file mode 100644 index 0000000..0fc3451 --- /dev/null +++ b/src/FeedbackBot/Pages/Error.cshtml.cs @@ -0,0 +1,26 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace FeedbackBot.Pages; + +[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] +[IgnoreAntiforgeryToken] +public class ErrorModel : PageModel +{ + public string? RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + private readonly ILogger _logger; + + public ErrorModel(ILogger logger) + { + _logger = logger; + } + + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + } +} diff --git a/src/FeedbackBot/Pages/Index.razor b/src/FeedbackBot/Pages/Index.razor new file mode 100644 index 0000000..ff9326a --- /dev/null +++ b/src/FeedbackBot/Pages/Index.razor @@ -0,0 +1,69 @@ +@page "/" + +Welcome - FeedbackBot + +
+

Welcome

+ +

Welcome to feedback! This is a website to keep track of user feedback of various applications of UC Davis.

+
+
+
+
+

PrePurchasing

+

PrePurchasing allows users to submit any type of order request, including KFS, MyTravel, + and other campus services.

+ + + +
+
+
+
+

ACE

+

Academic Course Evaluation (ACE) makes course evaluations easier for students, faculty, and + staff.

+ + + +
+
+
+
+

Commencement

+

Commencement allows staff to manage student's requests pertaining to Commencement + activities.

+ + + +
+
+
+
+

FeedbackBot

+

Hello this is an app called feedbackbot it does stuff related to giving feedback + automatically to our team directly into the github repo

+ + + +
+
+
+
+

Online Registration

+

Online Registration allows departments to manage and collect fees for events.

+ + + +
+
+
+
+

PEAKS

+

People, Equipment, Access, Keys and Space Tracking application

+ + + +
+
+
diff --git a/src/FeedbackBot/Pages/_Host.cshtml b/src/FeedbackBot/Pages/_Host.cshtml new file mode 100644 index 0000000..96f73fb --- /dev/null +++ b/src/FeedbackBot/Pages/_Host.cshtml @@ -0,0 +1,8 @@ +@page "/" +@namespace FeedbackBot.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@{ + Layout = "_Layout"; +} + + diff --git a/src/FeedbackBot/Pages/_Layout.cshtml b/src/FeedbackBot/Pages/_Layout.cshtml new file mode 100644 index 0000000..32c7970 --- /dev/null +++ b/src/FeedbackBot/Pages/_Layout.cshtml @@ -0,0 +1,48 @@ +@using Microsoft.AspNetCore.Components.Web +@namespace FeedbackBot.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + + + + + + + + + + + + + + + + + @RenderBody() + +
+ + An error has occurred. This application may no longer respond until reloaded. + + + An unhandled exception has occurred. See browser dev tools for details. + + Reload + 🗙 +
+ + + + + diff --git a/src/FeedbackBot/Program.cs b/src/FeedbackBot/Program.cs index 500ee0b..72dd6e3 100644 --- a/src/FeedbackBot/Program.cs +++ b/src/FeedbackBot/Program.cs @@ -1,21 +1,71 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using FeedbackBot; +using FeedbackBot.Services; -namespace FeedbackBot +var builder = WebApplication.CreateBuilder(args); + +builder.Services.Configure(builder.Configuration.GetSection("AppSettings")); + +// Add services to the container. +builder.Services.AddAuthentication(options => { - public class Program + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; +}) +.AddCookie() +.AddOpenIdConnect(oidc => +{ + oidc.ClientId = builder.Configuration["Authentication:ClientId"]; + oidc.ClientSecret = builder.Configuration["Authentication:ClientSecret"]; + oidc.Authority = builder.Configuration["Authentication:Authority"]; + oidc.ResponseType = OpenIdConnectResponseType.Code; + oidc.Scope.Add("openid"); + oidc.Scope.Add("profile"); + oidc.Scope.Add("email"); + oidc.Scope.Add("eduPerson"); + oidc.TokenValidationParameters = new TokenValidationParameters { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } + NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" + }; +}); + +builder.Services.AddAuthorization(options => +{ + // By default, all incoming requests will be authorized according to the default policy + options.FallbackPolicy = options.DefaultPolicy; +}); + +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); + +// infrastructure +builder.Services.AddTransient(); +builder.Services.AddScoped(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); } + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); + +app.Run(); diff --git a/src/FeedbackBot/Properties/launchSettings.json b/src/FeedbackBot/Properties/launchSettings.json index 5ed32f6..f497b59 100644 --- a/src/FeedbackBot/Properties/launchSettings.json +++ b/src/FeedbackBot/Properties/launchSettings.json @@ -1,27 +1,29 @@ -{ +{ "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:57993", - "sslPort": 44310 + "applicationUrl": "http://localhost:17181", + "sslPort": 44391 } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", + "FeedbackBot": { + "commandName": "Project", + "dotnetRunMessages": true, "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "hotReloadProfile": "aspnetcore" }, - "FeedbackBot": { - "commandName": "Project", + "IIS Express": { + "commandName": "IISExpress", "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } -} \ No newline at end of file +} diff --git a/src/FeedbackBot/Services/GitHubService.cs b/src/FeedbackBot/Services/GitHubService.cs index bb51b8f..f678a76 100644 --- a/src/FeedbackBot/Services/GitHubService.cs +++ b/src/FeedbackBot/Services/GitHubService.cs @@ -1,85 +1,82 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using Microsoft.Extensions.Options; using Octokit; -namespace FeedbackBot.Services +namespace FeedbackBot.Services; + +public interface IGitHubService { - public interface IGitHubService - { - Task> GetIssues(string appName); + Task> GetIssues(string appName); - Task CreateIssue(string appName, NewIssue issue); + Task CreateIssue(string appName, NewIssue issue); - Task GetIssue(string appName, int id); + Task GetIssue(string appName, int id); - Task UpdateIssue(string appName, int id, IssueUpdate update); + Task UpdateIssue(string appName, int id, IssueUpdate update); - Task> GetComments(string appName, int issueId); + Task> GetComments(string appName, int issueId); - Task AddComment(string appName, int issueId, string comment); + Task AddComment(string appName, int issueId, string comment); - Task Search(string appName, string query); - } + Task Search(string appName, string query); +} - public class GitHubService : IGitHubService +public class GitHubService : IGitHubService +{ + public GitHubClient client; + + public GitHubService(IOptions appSettings) { - public GitHubClient client; + var settings = appSettings.Value; - public GitHubService(IOptions appSettings) - { - var settings = appSettings.Value; + client = new GitHubClient(new ProductHeaderValue("FeedbackBot")); - client = new GitHubClient(new ProductHeaderValue("FeedbackBot")); - - client.Credentials = new Credentials(settings.GitHubToken); - } + client.Credentials = new Credentials(settings.GitHubToken); + } - public async Task> GetIssues(string appName) + public async Task> GetIssues(string appName) + { + var settings = new RepositoryIssueRequest { - var settings = new RepositoryIssueRequest - { - Filter = IssueFilter.Created, - Labels = { "feedback" } - }; + Filter = IssueFilter.Created, + Labels = { "feedback" } + }; - return await client.Issue.GetAllForRepository("ucdavis", appName, settings); - } + return await client.Issue.GetAllForRepository("ucdavis", appName, settings); + } - public async Task CreateIssue(string appName, NewIssue issue) - { - return await client.Issue.Create("ucdavis", appName, issue); - } + public async Task CreateIssue(string appName, NewIssue issue) + { + return await client.Issue.Create("ucdavis", appName, issue); + } - public async Task GetIssue(string appName, int id) - { - return await client.Issue.Get("ucdavis", appName, id); - } + public async Task GetIssue(string appName, int id) + { + return await client.Issue.Get("ucdavis", appName, id); + } - public Task UpdateIssue(string appName, int id, IssueUpdate update) - { - return client.Issue.Update("ucdavis", appName, id, update); - } + public Task UpdateIssue(string appName, int id, IssueUpdate update) + { + return client.Issue.Update("ucdavis", appName, id, update); + } - public async Task> GetComments(string appName, int issueId) - { - return await client.Issue.Comment.GetAllForIssue("ucdavis", appName, issueId); - } + public async Task> GetComments(string appName, int issueId) + { + return await client.Issue.Comment.GetAllForIssue("ucdavis", appName, issueId); + } - public async Task AddComment(string appName, int issueId, string comment) - { - return await client.Issue.Comment.Create("ucdavis", appName, issueId, comment); - } + public async Task AddComment(string appName, int issueId, string comment) + { + return await client.Issue.Comment.Create("ucdavis", appName, issueId, comment); + } - public async Task Search(string appName, string query) - { - var request = new SearchIssuesRequest(query); - request.Repos.Add("ucdavis", appName); - request.Labels = new List() { "feedback" }; ; - request.State = ItemState.Open; + public async Task Search(string appName, string query) + { + var request = new SearchIssuesRequest(query); + request.Repos.Add("ucdavis", appName); + request.Labels = new List() { "feedback" }; ; + request.State = ItemState.Open; - return await client.Search.SearchIssues(request); - } + return await client.Search.SearchIssues(request); } } + diff --git a/src/FeedbackBot/Services/GitHubServiceExtensions.cs b/src/FeedbackBot/Services/GitHubServiceExtensions.cs new file mode 100644 index 0000000..dc187d5 --- /dev/null +++ b/src/FeedbackBot/Services/GitHubServiceExtensions.cs @@ -0,0 +1,27 @@ +using FeedbackBot.Models; +using Octokit; + +namespace FeedbackBot.Services; + +public static class GitHubServiceExtensions +{ + public static CommentContainer? ToIssueCommentContainer(this IssueComment comment) + { + var indexOfLine = comment.Body.IndexOf("--------------------"); + if (indexOfLine == -1) + { + // not a FeedbackBot comment + return null; + } + + var bodycomment = comment.Body.Substring(0, indexOfLine); + if (String.IsNullOrWhiteSpace(bodycomment)) + { + return null; + } + + var commentContainer = new CommentContainer(); + commentContainer.Deserialize(comment, bodycomment); + return commentContainer; + } +} diff --git a/src/FeedbackBot/Services/UserService.cs b/src/FeedbackBot/Services/UserService.cs new file mode 100644 index 0000000..684c9e8 --- /dev/null +++ b/src/FeedbackBot/Services/UserService.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Components.Authorization; + +namespace FeedbackBot.Services; + +public class UserService: IUserService +{ + private AuthenticationStateProvider _authStateProvider; + + public UserService(AuthenticationStateProvider authStateProvider) => _authStateProvider = authStateProvider; + + public async Task GetKerberos() + { + var authState = await _authStateProvider.GetAuthenticationStateAsync(); + return authState?.User?.Identity?.Name; + } + + public async Task IsAuthenticated() + { + var authState = await _authStateProvider.GetAuthenticationStateAsync(); + return authState?.User?.Identity?.IsAuthenticated ?? false; + } +} + +public interface IUserService +{ + Task GetKerberos(); + Task IsAuthenticated(); +} \ No newline at end of file diff --git a/src/FeedbackBot/Shared/CreateIssue.razor b/src/FeedbackBot/Shared/CreateIssue.razor new file mode 100644 index 0000000..45bf79b --- /dev/null +++ b/src/FeedbackBot/Shared/CreateIssue.razor @@ -0,0 +1,60 @@ +@inject IGitHubService gitHubService +@inject IUserService userService +@inject NavigationManager navigationManager + +
+ @*

Submit a new feedback for @appName

*@ +
+ + @*
+

AUserName

+
*@ + + +
+
+ + + +
+
+
+ +@code { + [CascadingParameter(Name = "appName")] + public string appName { get; set; } = ""; + + private FormModel formModel = new(); + + async Task CreateNewIssue() + { + if (!await userService.IsAuthenticated()) + { + return; + } + + var kerberos = await userService.GetKerberos(); + + // Initialize new issue + var newIssue = new NewIssue(formModel.Title) + { + Body = $"{formModel.Description}\r\n" + + "--------------------\r\n" + + "Votes: 1\r\n" + + $"Author: {kerberos}\r\n" + + $"Voters: {kerberos}", + Labels = { "feedback" } + }; + var issue = await gitHubService.CreateIssue(appName, newIssue); + navigationManager.NavigateTo($"/app/{appName}"); + } + + private class FormModel + { + [Required] + public string Title { get; set; } = ""; + + [Required] + public string Description { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/FeedbackBot/Shared/DisplayIssues.razor b/src/FeedbackBot/Shared/DisplayIssues.razor new file mode 100644 index 0000000..69d6e02 --- /dev/null +++ b/src/FeedbackBot/Shared/DisplayIssues.razor @@ -0,0 +1,72 @@ +@if (@appIssues != null) +{ + int i = 0; + @foreach (var issue in @appIssues) + { +
+
+ +
+
+ +

@issue.Title

+
+
+
+

by @issue.Author

+ +
+
+
+
+ @issue.Body
+
+
+ +
+

+ Upvoted by: + @issue.StringOfVoters +

+
+ +
+ +
+
+ i++; + } +} + +@code { + [Parameter] + public BulkObservableCollection? appIssues { get; set; } + + [CascadingParameter(Name = "appName")] + public string appName { get; set; } = ""; + + override protected void OnInitialized() + { + if (appIssues != null) + { + appIssues.CollectionChanged += (sender, e) => + { + StateHasChanged(); + }; + } + } + + private void Voted(IssueContainer currentIssue, IssueContainer newIssue) + { + if (appIssues != null) + { + appIssues[appIssues.IndexOf(currentIssue)] = newIssue; + StateHasChanged(); + } + } +} \ No newline at end of file diff --git a/src/FeedbackBot/Shared/MainLayout.razor b/src/FeedbackBot/Shared/MainLayout.razor new file mode 100644 index 0000000..619e491 --- /dev/null +++ b/src/FeedbackBot/Shared/MainLayout.razor @@ -0,0 +1,27 @@ +@inherits LayoutComponentBase + +FeedbackBot + + +
+ @Body +
+
+

© 2016 - FeedbackBot

+
+
\ No newline at end of file diff --git a/src/FeedbackBot/Shared/SearchBar.razor b/src/FeedbackBot/Shared/SearchBar.razor new file mode 100644 index 0000000..fb63917 --- /dev/null +++ b/src/FeedbackBot/Shared/SearchBar.razor @@ -0,0 +1,48 @@ +@inject IGitHubService gitHubService +@inject IUserService userService + +
+
+ + + + + + +
+
+ +@code { + [CascadingParameter(Name = "appName")] + public string appName { get; set; } = ""; + + [Parameter] + public BulkObservableCollection? appIssues { get; set; } + + private FormModel formModel = new(); + + async Task DoSearch() + { + if (!await userService.IsAuthenticated() || string.IsNullOrEmpty(appName) || appIssues == null) + { + return; + } + + var kerberos = await userService.GetKerberos(); + var results = await gitHubService.Search(appName, formModel.Search); + + appIssues.Clear(); + appIssues.AddRange(results.Items.Select(i => + { + var newIssueContainer = new IssueContainer { Kerberos = kerberos }; + newIssueContainer.Deserialize(i); + return newIssueContainer; + })); + } + + private class FormModel + { + [Required] + public string Search { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/FeedbackBot/Shared/VoteButton.razor b/src/FeedbackBot/Shared/VoteButton.razor new file mode 100644 index 0000000..3c6fbf2 --- /dev/null +++ b/src/FeedbackBot/Shared/VoteButton.razor @@ -0,0 +1,63 @@ +@inject IGitHubService gitHubService +@inject IUserService userService + +
+ +

|

+

@issueDetails?.NumOfVotes

+
+ +@code { + [CascadingParameter(Name = "appName")] + public string appName { get; set; } = ""; + + [Parameter] + public IssueContainer? issueDetails { get; set; } + + [Parameter] + public Action? OnVoted { get; set; } + + private async Task Vote(int? id) + { + if (!await userService.IsAuthenticated() || string.IsNullOrEmpty(appName) || id == null || issueDetails == null) + { + return; + } + + var kerberos = await userService.GetKerberos() ?? ""; + + var issue = await gitHubService.GetIssue(appName, id.Value); + var newIssueContainer = new IssueContainer { Kerberos = kerberos }; + + newIssueContainer.Deserialize(issue); + + if (newIssueContainer.VoteState == "unvote") + { + var newVoteCount = newIssueContainer.NumOfVotesInt - 1; + newIssueContainer.NumOfVotesInt = newVoteCount; + newIssueContainer.NumOfVotes = newVoteCount.ToString(); + newIssueContainer.ListOfVoters.Remove(kerberos); + } + else + { + // Update vote count + var newVoteCount = newIssueContainer.NumOfVotesInt + 1; + newIssueContainer.NumOfVotesInt = newVoteCount; + newIssueContainer.NumOfVotes = newVoteCount.ToString(); + newIssueContainer.ListOfVoters.Add(kerberos); + } + newIssueContainer.StringOfVoters = string.Join(",", newIssueContainer.ListOfVoters.ToArray()).Trim().TrimStart(','); + + // Update issue on gitHubService + var update = issue.ToUpdate(); + update.Body = newIssueContainer.Serialize(); + await gitHubService.UpdateIssue(appName, id.Value, update); + + issueDetails = newIssueContainer; + + StateHasChanged(); + OnVoted?.Invoke(newIssueContainer); + } + +} \ No newline at end of file diff --git a/src/FeedbackBot/Startup.cs b/src/FeedbackBot/Startup.cs deleted file mode 100644 index ef79cba..0000000 --- a/src/FeedbackBot/Startup.cs +++ /dev/null @@ -1,92 +0,0 @@ -using AspNetCore.Security.CAS; -using FeedbackBot.Models; -using FeedbackBot.Services; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace FeedbackBot -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - // global configuration - services.AddSingleton(_ => Configuration); - services.Configure(Configuration.GetSection("AppSettings")); - services.Configure(Configuration.GetSection("Authentication")); - - // infrastructure - services.AddTransient(); - - // caching - services.AddDistributedMemoryCache(); - - // sessions - services.AddSession(/* options go here */); - - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = SameSiteMode.None; - }); - - // add cas auth backed by a cookie signin scheme - services.AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }) - .AddCookie(options => - { - options.LoginPath = new PathString("/login"); - }) - .AddCAS(options => - { - options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; - options.CasServerUrlBase = Configuration["Authentication:CasBaseUrl"]; - }); - - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - app.UseHttpsRedirection(); - app.UseStaticFiles(); - app.UseCookiePolicy(); - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(routes => { - routes.MapDefaultControllerRoute(); - }); - } - } -} diff --git a/src/FeedbackBot/Utilities/BulkObservableCollection.cs b/src/FeedbackBot/Utilities/BulkObservableCollection.cs new file mode 100644 index 0000000..1e45852 --- /dev/null +++ b/src/FeedbackBot/Utilities/BulkObservableCollection.cs @@ -0,0 +1,39 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; + +namespace FeedbackBot.Utilities; + +/// +/// Adds AddRange method to ObservableCollection. +/// +/// +public class BulkObservableCollection : ObservableCollection +{ + public BulkObservableCollection() : base() { } + + public BulkObservableCollection(IEnumerable collection) : base(collection) { } + + public BulkObservableCollection(List list) : base(list) { } + + public void AddRange(IEnumerable range) + { + var startIndex = this.Count; + + foreach (var item in range) + { + Items.Add(item); + } + + this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); + this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, range, startIndex)); + } + + public void Reset(IEnumerable range) + { + this.Items.Clear(); + + AddRange(range); + } +} diff --git a/src/FeedbackBot/Views/Home/Details.cshtml b/src/FeedbackBot/Views/Home/Details.cshtml deleted file mode 100644 index 287f6ab..0000000 --- a/src/FeedbackBot/Views/Home/Details.cshtml +++ /dev/null @@ -1,84 +0,0 @@ -@model FeedbackBot.Models.IssueDetailsViewModel - -@{ - ViewData["Title"] = "Issue"; -} - -

@ViewData["Message"]

- - -@if (Model.VoteMessage != null) -{ - -} -
- -
- Back -
- -
-
-
-
-

@Model.Issue.Title

- -
-

by @Model.Issue.Author

-
- -
-
- @Model.Issue.Body
-
- -
-

- Upvoted by: - @Model.Issue.StringOfVoters -

-
-
- @using (Html.BeginForm("vote", "Home")) - { - - - - - } -

|

-

@Model.Issue.NumOfVotes

-
-
-
-
-
-
-
-

Submit New Comment


-
- @using (Html.BeginForm("addComment", "Home")) - { -
- -
-
- } -
-
- -
- @foreach (var comment in Model.Comments) - { -
-
-
-
Date Created: @comment.CreateDate

-
Comment: @comment.Body

-
Author: @comment.Author


-
-
-
- } -
-
diff --git a/src/FeedbackBot/Views/Home/DisplayIssuesPartial.cshtml b/src/FeedbackBot/Views/Home/DisplayIssuesPartial.cshtml deleted file mode 100644 index fcadb0a..0000000 --- a/src/FeedbackBot/Views/Home/DisplayIssuesPartial.cshtml +++ /dev/null @@ -1,48 +0,0 @@ -@model IReadOnlyList - -@foreach (var issue in Model) -{ -
-
- -
-
-

@issue.Title

-
-
-

by @issue.Author

- -
-
-
-
- @issue.Body
-
-
- -
-

- Upvoted by: - @issue.StringOfVoters -

-
-
-
- @using (Html.BeginForm("vote", "Home")) - { - - - - } -

|

-

@issue.NumOfVotes

-
- - - -
-
- -
-
-} diff --git a/src/FeedbackBot/Views/Home/Index.cshtml b/src/FeedbackBot/Views/Home/Index.cshtml deleted file mode 100644 index 4d7839d..0000000 --- a/src/FeedbackBot/Views/Home/Index.cshtml +++ /dev/null @@ -1,64 +0,0 @@ - @{ - ViewData["Title"] = "Welcome"; -} -
-

@ViewData["Message"]

- -

Welcome to feedback! This is a website to keep track of user feedback of various applications of UC Davis.

-
-
-
-
-

PrePurchasing

-

PrePurchasing allows users to submit any type of order request, including KFS, MyTravel, and other campus services.

- - - -
-
-
-
-

ACE

-

Academic Course Evaluation (ACE) makes course evaluations easier for students, faculty, and staff.

- - - -
-
-
-
-

Commencement

-

Commencement allows staff to manage student's requests pertaining to Commencement activities.

- - - -
-
-
-
-

FeedbackBot

-

Hello this is an app called feedbackbot it does stuff related to giving feedback automatically to our team directly into the github repo

- - - -
-
-
-
-

Online Registration

-

Online Registration allows departments to manage and collect fees for events.

- - - -
-
-
-
-

PEAKS

-

People, Equipment, Access, Keys and Space Tracking application

- - - -
-
-
diff --git a/src/FeedbackBot/Views/Home/NewIssuePartial.cshtml b/src/FeedbackBot/Views/Home/NewIssuePartial.cshtml deleted file mode 100644 index 12f9138..0000000 --- a/src/FeedbackBot/Views/Home/NewIssuePartial.cshtml +++ /dev/null @@ -1,18 +0,0 @@ - -
-
- @using (Html.BeginForm("createIssue", "Home", new { appName = ViewData["AppName"] })) - { - - -
-

-
- - -
-
- - } -
-
\ No newline at end of file diff --git a/src/FeedbackBot/Views/Home/Search.cshtml b/src/FeedbackBot/Views/Home/Search.cshtml deleted file mode 100644 index 866618c..0000000 --- a/src/FeedbackBot/Views/Home/Search.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@model IReadOnlyList - -
- @{ - ViewData["Title"] = "Home Page"; - } - -
- - Back - -

Search Results For @ViewData["SearchTerm"] in @ViewData["AppName"]

- - @{ - Html.RenderPartial("SearchBarPartial"); - } - - @{ - Html.RenderPartial("DisplayIssuesPartial"); - } -
diff --git a/src/FeedbackBot/Views/Home/SearchBarPartial.cshtml b/src/FeedbackBot/Views/Home/SearchBarPartial.cshtml deleted file mode 100644 index 689596a..0000000 --- a/src/FeedbackBot/Views/Home/SearchBarPartial.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -
-
- @using (Html.BeginForm("search", "Home", new { appName = ViewData["AppName"] })) - { - - - - } -
-
\ No newline at end of file diff --git a/src/FeedbackBot/Views/Home/app.cshtml b/src/FeedbackBot/Views/Home/app.cshtml deleted file mode 100644 index ed2550c..0000000 --- a/src/FeedbackBot/Views/Home/app.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@model IReadOnlyList - -
- @{ - ViewData["Title"] = "Home Page"; -

Submit a new feedback for @ViewData["AppName"]

- } - - @{ - Html.RenderPartial("SearchBarPartial"); - } - - @{ - Html.RenderPartial("NewIssuePartial"); - } - -

@ViewData["AppName"]

- - @{ - Html.RenderPartial("DisplayIssuesPartial"); - } -
diff --git a/src/FeedbackBot/Views/Shared/Error.cshtml b/src/FeedbackBot/Views/Shared/Error.cshtml deleted file mode 100644 index e514139..0000000 --- a/src/FeedbackBot/Views/Shared/Error.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. -

diff --git a/src/FeedbackBot/Views/Shared/_Layout.cshtml b/src/FeedbackBot/Views/Shared/_Layout.cshtml deleted file mode 100644 index fe61f31..0000000 --- a/src/FeedbackBot/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - @ViewData["Title"] - FeedbackBot - - - - - - - - - -
- @RenderBody() -
-
-

© 2016 - FeedbackBot

-
-
- - - - - - @RenderSection("scripts", required: false) - - diff --git a/src/FeedbackBot/Views/Shared/css_browser_selector.js b/src/FeedbackBot/Views/Shared/css_browser_selector.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/FeedbackBot/Views/_ViewImports.cshtml b/src/FeedbackBot/Views/_ViewImports.cshtml deleted file mode 100644 index 906b587..0000000 --- a/src/FeedbackBot/Views/_ViewImports.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@using FeedbackBot -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/src/FeedbackBot/Views/_ViewStart.cshtml b/src/FeedbackBot/Views/_ViewStart.cshtml deleted file mode 100644 index a5f1004..0000000 --- a/src/FeedbackBot/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/src/FeedbackBot/_Imports.razor b/src/FeedbackBot/_Imports.razor new file mode 100644 index 0000000..34c055f --- /dev/null +++ b/src/FeedbackBot/_Imports.razor @@ -0,0 +1,18 @@ +@using System.Net.Http +@using System.Collections.ObjectModel +@using System.Collections.Generic +@using System.ComponentModel.DataAnnotations +@using System.Linq +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using FeedbackBot +@using FeedbackBot.Models +@using FeedbackBot.Shared +@using FeedbackBot.Services +@using FeedbackBot.Utilities +@using Octokit diff --git a/src/FeedbackBot/appsettings.Development.json b/src/FeedbackBot/appsettings.Development.json index a2880cb..f042c67 100644 --- a/src/FeedbackBot/appsettings.Development.json +++ b/src/FeedbackBot/appsettings.Development.json @@ -1,9 +1,9 @@ { + "DetailedErrors": true, "Logging": { "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Default": "Information", + "Microsoft.AspNetCore": "Warning" } } } diff --git a/src/FeedbackBot/appsettings.json b/src/FeedbackBot/appsettings.json index 7b343a4..0a3c24e 100644 --- a/src/FeedbackBot/appsettings.json +++ b/src/FeedbackBot/appsettings.json @@ -5,14 +5,15 @@ "GitHubToken": "" }, "Authentication": { - "CasBaseUrl": "https://ssodev.ucdavis.edu/cas/" + "ClientId": "lN9ycqqv6GVHRwb2qb9NIIBt5dR0VxGFpW4u", + "ClientSecret": "rP0Uuz5f9Eoz1p7yuvdQoY72a7PjgHledned", + "Authority": "https://cas.ucdavis.edu/cas/oidc" }, "Logging": { - "IncludeScopes": false, "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Default": "Information", + "Microsoft.AspNetCore": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/src/FeedbackBot/wwwroot/_references.js b/src/FeedbackBot/wwwroot/_references.js deleted file mode 100644 index 9f22612..0000000 --- a/src/FeedbackBot/wwwroot/_references.js +++ /dev/null @@ -1,7 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// diff --git a/src/FeedbackBot/wwwroot/css/site.css b/src/FeedbackBot/wwwroot/css/site.css index 398a3c9..04577ff 100644 --- a/src/FeedbackBot/wwwroot/css/site.css +++ b/src/FeedbackBot/wwwroot/css/site.css @@ -480,3 +480,32 @@ body { width: 100%; } } + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } diff --git a/src/FeedbackBot/wwwroot/css/site.min.css b/src/FeedbackBot/wwwroot/css/site.min.css index bab8e5e..adc1fe5 100644 --- a/src/FeedbackBot/wwwroot/css/site.min.css +++ b/src/FeedbackBot/wwwroot/css/site.min.css @@ -476,3 +476,32 @@ body { width: 100%; } } + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } diff --git a/src/FeedbackBot/wwwroot/favicon.ico b/src/FeedbackBot/wwwroot/favicon.ico index a3a799985c43bc7309d701b2cad129023377dc71..63e859b476eff5055e0e557aaa151ca8223fbeef 100644 GIT binary patch delta 57 zcmZ4Xi*cKl8Uq6(0~3RS0*F>%SRuf`02E>n0Ma0S1`7kjIUsxUI+is88@<$6Cf^XT F0{{n`3U~kj literal 32038 zcmeHwX>eTEbtY7aYbrGrkNjgie?1jXjZ#zP%3n{}GObKv$BxI7Sl;Bwl5E+Qtj&t8 z*p|m4DO#HoJC-FyvNnp8NP<{Na0LMnTtO21(rBP}?EAiNjWgeO?z`{3ZoURUQlV2d zY1Pqv{m|X_oO91|?^z!6@@~od!@OH>&BN;>c@O+yUfy5w>LccTKJJ&`-k<%M^Zvi( z<$dKp=jCnNX5Qa+M_%6g|IEv~4R84q9|7E=|Ho(Wz3f-0wPjaRL;W*N^>q%^KGRr7 zxbjSORb_c&eO;oV_DZ7ua!sPH=0c+W;`vzJ#j~-x3uj};50#vqo*0w4!LUqs*UCh9 zvy2S%$#8$K4EOa&e@~aBS65_hc~Mpu=454VT2^KzWqEpBA=ME|O;1cn?8p<+{MKJf zbK#@1wzL44m$k(?85=Obido7=C|xWKe%66$z)NrzRwR>?hK?_bbwT z@Da?lBrBL}Zemo1@!9pYRau&!ld17h{f+UV0sY(R{ET$PBB|-=Nr@l-nY6w8HEAw* zRMIQU`24Jl_IFEPcS=_HdrOP5yf81z_?@M>83Vv65$QFr9nPg(wr`Ke8 zaY4ogdnMA*F7a4Q1_uXadTLUpCk;$ZPRRJ^sMOch;rlbvUGc1R9=u;dr9YANbQ<4Z z#P|Cp9BP$FXNPolgyr1XGt$^lFPF}rmBF5rj1Kh5%dforrP8W}_qJL$2qMBS-#%-|s#BPZBSETsn_EBYcr(W5dq( z@f%}C|iN7)YN`^)h7R?Cg}Do*w-!zwZb9=BMp%Wsh@nb22hA zA{`wa8Q;yz6S)zfo%sl08^GF`9csI9BlGnEy#0^Y3b);M+n<(}6jziM7nhe57a1rj zC@(2ISYBL^UtWChKzVWgf%4LW2Tqg_^7jMw`C$KvU+mcakFjV(BGAW9g%CzSyM;Df z143=mq0oxaK-H;o>F3~zJ<(3-j&?|QBn)WJfP#JR zRuA;`N?L83wQt78QIA$(Z)lGQY9r^SFal;LB^qi`8%8@y+mwcGsf~nv)bBy2S7z~9 z=;X@Gglk)^jpbNz?1;`!J3QUfAOp4U$Uxm5>92iT`mek#$>s`)M>;e4{#%HAAcb^8_Ax%ersk|}# z0bd;ZPu|2}18KtvmIo8`1@H~@2ejwo(5rFS`Z4&O{$$+ch2hC0=06Jh`@p+p8LZzY z&2M~8T6X^*X?yQ$3N5EzRv$(FtSxhW>>ABUyp!{484f8(%C1_y)3D%Qgfl_!sz`LTXOjR&L!zPA0qH_iNS!tY{!^2WfD%uT}P zI<~&?@&))5&hPPHVRl9);TPO>@UI2d!^ksb!$9T96V(F){puTsn(}qt_WXNw4VvHj zf;6A_XCvE`Z@}E-IOaG0rs>K>^=Sr&OgT_p;F@v0VCN0Y$r|Lw1?Wjt`AKK~RT*kJ z2>QPuVgLNcF+XKno;WBv$yj@d_WFJbl*#*V_Cwzo@%3n5%z4g21G*PVZ)wM5$A{klYozmGlB zT@u2+s}=f}25%IA!yNcXUr!!1)z(Nqbhojg0lv@7@0UlvUMT)*r;M$d0-t)Z?B1@qQk()o!4fqvfr_I0r7 zy1(NdkHEj#Yu{K>T#We#b#FD=c1XhS{hdTh9+8gy-vkcdkk*QS@y(xxEMb1w6z<^~ zYcETGfB#ibR#ql0EiD;PR$L&Vrh2uRv5t_$;NxC;>7_S5_OXxsi8udY3BUUdi55Sk zcyKM+PQ9YMA%D1kH1q48OFG(Gbl=FmV;yk8o>k%0$rJ8%-IYsHclnYuTskkaiCGkUlkMY~mx&K}XRlKIW;odWIeuKjtbc^8bBOTqK zjj(ot`_j?A6y_h%vxE9o*ntx#PGrnK7AljD_r58ylE*oy@{IY%+mA^!|2vW_`>`aC{#3`#3;D_$^S^cM zRcF+uTO2sICledvFgNMU@A%M)%8JbSLq{dD|2|2Sg8vvh_uV6*Q?F&rKaV{v_qz&y z`f;stIb?Cb2!Cg7CG91Bhu@D@RaIrq-+o+T2fwFu#|j>lD6ZS9-t^5cx>p|?flqUA z;Cgs#V)O#`Aw4$Kr)L5?|7f4izl!;n0jux}tEW$&&YBXz9o{+~HhoiYDJ`w5BVTl&ARya=M7zdy$FEe}iGBur8XE>rhLj&_yDk5D4n2GJZ07u7%zyAfNtOLn;)M?h*Py-Xtql5aJOtL4U8e|!t? z((sc6&OJXrPdVef^wZV&x=Z&~uA7^ix8rly^rEj?#d&~pQ{HN8Yq|fZ#*bXn-26P^ z5!)xRzYO9{u6vx5@q_{FE4#7BipS#{&J7*>y}lTyV94}dfE%Yk>@@pDe&F7J09(-0|wuI|$of-MRfK51#t@t2+U|*s=W; z!Y&t{dS%!4VEEi$efA!#<<7&04?kB}Soprd8*jYv;-Qj~h~4v>{XX~kjF+@Z7<t?^|i z#>_ag2i-CRAM8Ret^rZt*^K?`G|o>1o(mLkewxyA)38k93`<~4VFI?5VB!kBh%NNU zxb8K(^-MU1ImWQxG~nFB-Un;6n{lQz_FfsW9^H$Xcn{;+W^ZcG$0qLM#eNV=vGE@# z1~k&!h4@T|IiI<47@pS|i?Qcl=XZJL#$JKve;booMqDUYY{(xcdj6STDE=n?;fsS1 ze`h~Q{CT$K{+{t+#*I1=&&-UU8M&}AwAxD-rMa=e!{0gQXP@6azBq9(ji11uJF%@5 zCvV`#*?;ZguQ7o|nH%bm*s&jLej#@B35gy32ZAE0`Pz@#j6R&kN5w{O4~1rhDoU zEBdU)%Nl?8zi|DR((u|gg~r$aLYmGMyK%FO*qLvwxK5+cn*`;O`16c!&&XT{$j~5k zXb^fbh1GT-CI*Nj{-?r7HNg=e3E{6rxuluPXY z5Nm8ktc$o4-^SO0|Es_sp!A$8GVwOX+%)cH<;=u#R#nz;7QsHl;J@a{5NUAmAHq4D zIU5@jT!h?kUp|g~iN*!>jM6K!W5ar0v~fWrSHK@})@6Lh#h)C6F6@)&-+C3(zO! z8+kV|B7LctM3DpI*~EYo>vCj>_?x&H;>y0*vKwE0?vi$CLt zfSJB##P|M2dEUDBPKW=9cY-F;L;h3Fs4E2ERdN#NSL7ctAC z?-}_a{*L@GA7JHJudxtDVA{K5Yh*k(%#x4W7w+^ zcb-+ofbT5ieG+@QG2lx&7!MyE2JWDP@$k`M;0`*d+oQmJ2A^de!3c53HFcfW_Wtv< zKghQ;*FifmI}kE4dc@1y-u;@qs|V75Z^|Q0l0?teobTE8tGl@EB?k#q_wUjypJ*R zyEI=DJ^Z+d*&}B_xoWvs27LtH7972qqMxVFcX9}c&JbeNCXUZM0`nQIkf&C}&skSt z^9fw@b^Hb)!^hE2IJq~~GktG#ZWwWG<`@V&ckVR&r=JAO4YniJewVcG`HF;59}=bf zLyz0uxf6MhuSyH#-^!ZbHxYl^mmBVrx) zyrb8sQ*qBd_WXm9c~Of$&ZP$b^)<~0%nt#7y$1Jg$e}WCK>TeUB{P>|b1FAB?%K7>;XiOfd}JQ`|IP#Vf%kVy zXa4;XFZ+>n;F>uX&3|4zqWK2u3c<>q;tzjsb1;d{u;L$-hq3qe@82(ob<3qom#%`+ z;vzYAs7TIMl_O75BXu|r`Qhc4UT*vN$3Oo0kAC!{f2#HexDy|qUpgTF;k{o6|L>7l z=?`=*LXaow1o;oNNLXsGTrvC)$R&{m=94Tf+2iTT3Y_Or z-!;^0a{kyWtO4vksG_3cyc7HQ0~detf0+2+qxq(e1NS251N}w5iTSrM)`0p8rem!j zZ56hGD=pHI*B+dd)2B`%|9f0goozCSeXPw3 z+58k~sI02Yz#lOneJzYcG)EB0|F+ggC6D|B`6}d0khAK-gz7U3EGT|M_9$ZINqZjwf>P zJCZ=ogSoE`=yV5YXrcTQZx@Un(64*AlLiyxWnCJ9I<5Nc*eK6eV1Mk}ci0*NrJ=t| zCXuJG`#7GBbPceFtFEpl{(lTm`LX=B_!H+& z>$*Hf}}y zkt@nLXFG9%v**s{z&{H4e?aqp%&l#oU8lxUxk2o%K+?aAe6jLojA& z_|J0<-%u^<;NT*%4)n2-OdqfctSl6iCHE?W_Q2zpJken#_xUJlidzs249H=b#g z?}L4-Tnp6)t_5X?_$v)vz`s9@^BME2X@w<>sKZ3=B{%*B$T5Nj%6!-Hr;I!Scj`lH z&2dHFlOISwWJ&S2vf~@I4i~(0*T%OFiuX|eD*nd2utS4$1_JM?zmp>a#CsVy6Er^z zeNNZZDE?R3pM?>~e?H_N`C`hy%m4jb;6L#8=a7l>3eJS2LGgEUxsau-Yh9l~o7=Yh z2mYg3`m5*3Ik|lKQf~euzZlCWzaN&=vHuHtOwK!2@W6)hqq$Zm|7`Nmu%9^F6UH?+ z@2ii+=iJ;ZzhiUKu$QB()nKk3FooI>Jr_IjzY6=qxYy;&mvi7BlQ?t4kRjIhb|2q? zd^K~{-^cxjVSj?!Xs=Da5IHmFzRj!Kzh~b!?`P7c&T9s77VLYB?8_?F zauM^)p;qFG!9PHLfIsnt43UnmV?Wn?Ki7aXSosgq;f?MYUuSIYwOn(5vWhb{f%$pn z4ySN-z}_%7|B);A@PA5k*7kkdr4xZ@s{e9j+9w;*RFm;XPDQwx%~;8iBzSKTIGKO z{53ZZU*OLr@S5=k;?CM^i#zkxs3Sj%z0U`L%q`qM+tP zX$aL;*^g$7UyM2Go+_4A+f)IQcy^G$h2E zb?nT$XlgTEFJI8GN6NQf%-eVn9mPilRqUbT$pN-|;FEjq@Ao&TxpZg=mEgBHB zU@grU;&sfmqlO=6|G3sU;7t8rbK$?X0y_v9$^{X`m4jZ_BR|B|@?ZCLSPPEzz`w1n zP5nA;4(kQFKm%$enjkkBxM%Y}2si&d|62L)U(dCzCGn56HN+i#6|nV-TGIo0;W;`( zW-y=1KF4dp$$mC_|6}pbb>IHoKQeZajXQB>jVR?u`R>%l1o54?6NnS*arpVopdEF; zeC5J3*M0p`*8lif;!irrcjC?(uExejsi~>4wKYwstGY^N@KY}TujLx`S=Cu+T=!dx zKWlPm->I**E{A*q-Z^FFT5$G%7Ij0_*Mo4-y6~RmyTzUB&lfae(WZfO>um}mnsDXPEbau-!13!!xd!qh*{C)6&bz0j1I{>y$D-S)b*)JMCPk!=~KL&6Ngin0p6MCOxF2L_R9t8N!$2Wpced<#`y!F;w zKTi5V_kX&X09wAIJ#anfg9Dhn0s7(C6Nj3S-mVn(i|C6ZAVq0$hE)874co};g z^hR7pe4lU$P;*ggYc4o&UTQC%liCXooIfkI3TNaBV%t~FRr}yHu7kjQ2J*3;e%;iW zvDVCh8=G80KAeyhCuY2LjrC!Od1rvF7h}zszxGV)&!)6ChP5WAjv-zQAMNJIG!JHS zwl?pLxC-V5II#(hQ`l)ZAp&M0xd4%cxmco*MIk?{BD=BK`1vpc}D39|XlV z{c&0oGdDa~TL2FT4lh=~1NL5O-P~0?V2#ie`v^CnANfGUM!b4F=JkCwd7Q`c8Na2q zJGQQk^?6w}Vg9-{|2047((lAV84uN%sK!N2?V(!_1{{v6rdgZl56f0zDMQ+q)jKzzu^ztsVken;=DjAh6G`Cw`Q4G+BjS+n*=KI~^K{W=%t zbD-rN)O4|*Q~@<#@1Vx$E!0W9`B~IZeFn87sHMXD>$M%|Bh93rdGf1lKoX3K651t&nhsl= zXxG|%@8}Bbrlp_u#t*DZX<}_0Yb{A9*1Pd_)LtqNwy6xT4pZrOY{s?N4)pPwT(i#y zT%`lRi8U#Ken4fw>H+N`{f#FF?ZxFlLZg7z7#cr4X>id z{9kUD`d2=w_Zlb{^c`5IOxWCZ1k<0T1D1Z31IU0Q2edsZ1K0xv$pQVYq2KEp&#v#Z z?{m@Lin;*Str(C2sfF^L>{R3cjY`~#)m>Wm$Y|1fzeS0-$(Q^z@} zEO*vlb-^XK9>w&Ef^=Zzo-1AFSP#9zb~X5_+){$(eB4K z8gtW+nl{q+CTh+>v(gWrsP^DB*ge(~Q$AGxJ-eYc1isti%$%nM<_&Ev?%|??PK`$p z{f-PM{Ym8k<$$)(F9)tqzFJ?h&Dk@D?Dt{4CHKJWLs8$zy6+(R)pr@0ur)xY{=uXFFzH_> z-F^tN1y(2hG8V)GpDg%wW0Px_ep~nIjD~*HCSxDi0y`H!`V*~RHs^uQsb1*bK1qGpmd zB1m`Cjw0`nLBF2|umz+a#2X$c?Lj;M?Lj;MUp*d>7j~ayNAyj@SLpeH`)BgRH}byy zyQSat!;U{@O(<<2fp&oQkIy$z`_CQ-)O@RN;QD9T4y|wIJ^%U#(BF%=`i49}j!D-) zkOwPSJaG03SMkE~BzW}b_v>LA&y)EEYO6sbdnTX*$>UF|JhZ&^MSb4}Tgbne_4n+C zwI8U4i~PI>7a3{kVa8|))*%C0|K+bIbmV~a`|G#+`TU#g zXW;bWIcWsQi9c4X*RUDpIfyoPY)2bI-r9)xulm1CJDkQd6u+f)_N=w1ElgEBjprPF z3o?Ly0RVeY_{3~fPVckRMxe2lM8hj!B8F)JO z!`AP6>u>5Y&3o9t0QxBpNE=lJx#NyIbp1gD zzUYBIPYHIv9ngk-Zt~<)62^1Zs1LLYMh@_tP^I7EX-9)Ed0^@y{k65Gp0KRcTmMWw zU|+)qx{#q0SL+4q?Q`i0>COIIF8a0Cf&C`hbMj?LmG9K&iW-?PJt*u)38tTXAP>@R zZL6uH^!RYNq$p>PKz7f-zvg>OKXcZ8h!%Vo@{VUZp|+iUD_xb(N~G|6c#oQK^nHZU zKg#F6<)+`rf~k*Xjjye+syV{bwU2glMMMs-^ss4`bYaVroXzn`YQUd__UlZL_mLs z(vO}k!~(mi|L+(5&;>r<;|OHnbXBE78LruP;{yBxZ6y7K3)nMo-{6PCI7gQi6+rF_ zkPod!Z8n}q46ykrlQS|hVB(}(2Kf7BCZ>Vc;V>ccbk2~NGaf6wGQH@W9&?Zt3v(h*P4xDrN>ex7+jH*+Qg z%^jH$&+*!v{sQ!xkWN4+>|b}qGvEd6ANzgqoVy5Qfws}ef2QqF{iiR5{pT}PS&yjo z>lron#va-p=v;m>WB+XVz|o;UJFdjo5_!RRD|6W{4}A2a#bZv)gS_`b|KsSH)Sd_JIr%<%n06TX&t{&!H#{)?4W9hlJ`R1>FyugOh3=D_{einr zu(Wf`qTkvED+gEULO0I*Hs%f;&=`=X4;N8Ovf28x$A*11`dmfy2=$+PNqX>XcG`h% zJY&A6@&)*WT^rC(Caj}2+|X|6cICm5h0OK0cGB_!wEKFZJU)OQ+TZ1q2bTx9hxnq& z$9ee|f9|0M^)#E&Pr4)f?o&DMM4w>Ksb{hF(0|wh+5_{vPow{V%TFzU2za&gjttNi zIyR9qA56dX52Qbv2aY^g`U7R43-p`#sO1A=KS2aKgfR+Yu^bQ*i-qu z%0mP;Ap)B~zZgO9lG^`325gOf?iUHF{~7jyGC)3L(eL(SQ70VzR~wLN18tnx(Cz2~ zctBl1kI)wAe+cxWHw*NW-d;=pd+>+wd$a@GBju*wFvabSaPtHiT!o#QFC+wBVwYo3s=y;z1jM+M=Fj!FZM>UzpL-eZzOT( zhmZmEfWa=%KE#V3-ZK5#v!Hzd{zc^{ctF~- z>DT-U`}5!fk$aj24`#uGdB7r`>oX5tU|d*b|N3V1lXmv%MGrvE(dXG)^-J*LA>$LE z7kut4`zE)v{@Op|(|@i#c>tM!12FQh?}PfA0`Bp%=%*RiXVzLDXnXtE@4B)5uR}a> zbNU}q+712pIrM`k^odG8dKtG$zwHmQI^c}tfjx5?egx3!e%JRm_64e+>`Ra1IRfLb z1KQ`SxmH{cZfyVS5m(&`{V}Y4j6J{b17`h6KWqZ&hfc(oR zxM%w!$F(mKy05kY&lco3%zvLCxBW+t*rxO+i=qGMvobx0-<7`VUu)ka`){=ew+Ovt zg%52_{&UbkUA8aJPWsk)gYWV4`dnxI%s?7^fGpq{ZQuu=VH{-t7w~K%_E<8`zS;V- zKTho*>;UQQul^1GT^HCt@I-q?)&4!QDgBndn?3sNKYKCQFU4LGKJ$n@Je$&w9@E$X z^p@iJ(v&`1(tq~1zc>0Vow-KR&vm!GUzT?Eqgnc)leZ9p)-Z*C!zqb=-$XG0 z^!8RfuQs5s>Q~qcz92(a_Q+KH?C*vCTr~UdTiR`JGuNH8v(J|FTiSEcPrBpmHRtmd zI2Jng0J=bXK);YY^rM?jzn?~X-Pe`GbAy{D)Y6D&1GY-EBcy%Bq?bKh?A>DD9DD!p z?{q02wno2sraGUkZv5dx+J8)&K$)No43Zr(*S`FEdL!4C)}WE}vJd%{S6-3VUw>Wp z?Aasv`T0^%P$2vE?L+Qhj~qB~K%eW)xH(=b_jU}TLD&BP*Pc9hz@Z=e0nkpLkWl}> z_5J^i(9Z7$(XG9~I3sY)`OGZ#_L06+Dy4E>UstcP-rU@xJ$&rxvo!n1Ao`P~KLU-8 z{zDgN4-&A6N!kPSYbQ&7sLufi`YtE2uN$S?e&5n>Y4(q#|KP!cc1j)T^QrUXMPFaP z_SoYO8S8G}Z$?AL4`;pE?7J5K8yWqy23>cCT2{=-)+A$X^-I9=e!@J@A&-;Ufc)`H}c(VI&;0x zrrGv()5mjP%jXzS{^|29?bLNXS0bC%p!YXI!;O457rjCEEzMkGf~B3$T}dXBO23tP z+Ci>;5UoM?C@bU@f9G1^X3=ly&ZeFH<@|RnOG--A&)fd)AUgjw?%izq{p(KJ`EP0v z2mU)P!+3t@X14DA=E2RR-|p${GZ9ETX=d+kJRZL$nSa0daI@&oUUxnZg0xd_xu>Vz lzF#z5%kSKX?YLH3ll^(hI(_`L*t#Iva2Ede*Z;>H_ Date: Mon, 29 Nov 2021 14:23:04 -0800 Subject: [PATCH 2/2] Fix some refresh issues --- src/FeedbackBot/Pages/Details.razor | 2 ++ src/FeedbackBot/Pages/_Imports.razor | 1 + src/FeedbackBot/Shared/DisplayIssues.razor | 18 ++++++++++++------ src/FeedbackBot/Shared/SearchBar.razor | 1 + src/FeedbackBot/Shared/_Imports.razor | 1 + 5 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 src/FeedbackBot/Pages/_Imports.razor create mode 100644 src/FeedbackBot/Shared/_Imports.razor diff --git a/src/FeedbackBot/Pages/Details.razor b/src/FeedbackBot/Pages/Details.razor index d4b1d9b..bd93bd0 100644 --- a/src/FeedbackBot/Pages/Details.razor +++ b/src/FeedbackBot/Pages/Details.razor @@ -128,6 +128,7 @@ Comments = listOfComments, Issue = newIssueContainer }; + StateHasChanged(); } private void Voted(IssueContainer issue) @@ -169,6 +170,7 @@ } issueDetails.Comments.Insert(0, comment); + StateHasChanged(); } diff --git a/src/FeedbackBot/Pages/_Imports.razor b/src/FeedbackBot/Pages/_Imports.razor new file mode 100644 index 0000000..f2952a3 --- /dev/null +++ b/src/FeedbackBot/Pages/_Imports.razor @@ -0,0 +1 @@ +@namespace FeedbackBot.Pages diff --git a/src/FeedbackBot/Shared/DisplayIssues.razor b/src/FeedbackBot/Shared/DisplayIssues.razor index 69d6e02..b0c8a6e 100644 --- a/src/FeedbackBot/Shared/DisplayIssues.razor +++ b/src/FeedbackBot/Shared/DisplayIssues.razor @@ -6,7 +6,7 @@
-
+
@@ -30,7 +30,7 @@


- + @@ -61,11 +61,17 @@ } } - private void Voted(IssueContainer currentIssue, IssueContainer newIssue) + private void Voted(IssueContainer issue) { - if (appIssues != null) + if (issue != null && appIssues != null) { - appIssues[appIssues.IndexOf(currentIssue)] = newIssue; + // the +1 -1 thing is so that it returns -1 for missing instead of 0 + var i = appIssues.Where(x => x.Number == issue.Number).Select((x, i) => i + 1).SingleOrDefault() - 1; + if (i == -1) + { + return; + } + appIssues[i] = issue; StateHasChanged(); } } diff --git a/src/FeedbackBot/Shared/SearchBar.razor b/src/FeedbackBot/Shared/SearchBar.razor index fb63917..d7b1757 100644 --- a/src/FeedbackBot/Shared/SearchBar.razor +++ b/src/FeedbackBot/Shared/SearchBar.razor @@ -38,6 +38,7 @@ newIssueContainer.Deserialize(i); return newIssueContainer; })); + StateHasChanged(); } private class FormModel diff --git a/src/FeedbackBot/Shared/_Imports.razor b/src/FeedbackBot/Shared/_Imports.razor new file mode 100644 index 0000000..db75921 --- /dev/null +++ b/src/FeedbackBot/Shared/_Imports.razor @@ -0,0 +1 @@ +@namespace FeedbackBot.Shared