/// <summary> /// RPC-method for adding a course to the schedule and for /// adding the calling user to a course that someone else /// has already added. /// </summary> /// <param name="courseId">Course ID</param> /// <returns></returns> public async Task AddCourse(string courseId) { using (var errorReporter = new ErrorReporter(s => CallingClient.Errors.ScheduleMessage = s)) { var course = PaulRepository.Courses.FirstOrDefault(c => c.Id == courseId); if (course == null) { errorReporter.Throw( new ArgumentException("Course not found", nameof(courseId)), UserErrorsViewModel.GenericErrorMessage); } var schedule = CallingClient.SharedScheduleVM.Schedule; await CallingClient.SharedScheduleVM.TimetableHubSemaphore.WaitAsync(); try { var selectedCourse = schedule.SelectedCourses.FirstOrDefault(c => c.CourseId == courseId); if (course.IsTutorial) { // The user has decided to select a pending tutorial or one // that was already selected by another user, so remove all pending // tutorials of this course CallingClient.TailoredScheduleVM.RemovePendingTutorials(course, errorReporter); // Remove user from other possibly selected tutorial var parentCourse = schedule.SelectedCourses.First(sel => sel.Course.AllTutorials.SelectMany(it => it).Contains(course)); var otherSelectedTutorial = parentCourse.Course.AllTutorials .FirstOrDefault(group => group.Contains(course)) ?.FirstOrDefault(tut => schedule.SelectedCourses.Any(sel => Equals(tut, sel.Course) && sel.Users.Select(it => it.User).Contains(CallingClient.User))); if (otherSelectedTutorial != null) { await RemoveUserFromCourse(otherSelectedTutorial.Id, acquireSemaphore : false); } //If the user hasn't selected the parent course of the tutorial, it will be added here if (parentCourse.Users.All(u => u.User.Name != CallingClient.Name)) { var connectedCourses = parentCourse.Course.ConnectedCourses.Concat(new[] { parentCourse.Course }); foreach (var c in connectedCourses) { await PaulRepository.AddUserToSelectedCourseAsync(schedule.SelectedCourses.First(s => s.CourseId == c.Id), CallingClient.User); } } } if (selectedCourse == null) { var connectedCourses = course.ConnectedCourses.Concat(new[] { course }) .Select(it => PaulRepository.CreateSelectedCourse(schedule, CallingClient.User, it)) .ToList(); await PaulRepository.AddCourseToScheduleAsync(schedule, connectedCourses); AddTutorialsForCourse(courseId); } else if (selectedCourse.Users.All(u => u.User != CallingClient.User)) { // The course has already been added to the schedule by someone else. // Add the calling user to the selected course (if not yet done). await PaulRepository.AddUserToSelectedCourseAsync(selectedCourse, CallingClient.User); var connectedCourseIds = selectedCourse.Course.ConnectedCourses.Select(it => it.Id).ToList(); var selectedConnectedCourses = schedule.SelectedCourses .Where(selCo => connectedCourseIds.Contains(selCo.CourseId)); foreach (var connectedCourse in selectedConnectedCourses) { await PaulRepository.AddUserToSelectedCourseAsync(connectedCourse, CallingClient.User); } } UpdateAddedStateInSearchResultsAndCourseList(course, isAdded: true); } finally { CallingClient.SharedScheduleVM.TimetableHubSemaphore.Release(); } UpdateTailoredViewModels(); } }