コード例 #1
0
ファイル: QuestionViewModel.cs プロジェクト: bignis/Trivia
        public QuestionViewModel(Question question, IEnumerable<Answer> submittedAnswers, DispatchableObservableCollection<Note> notes)
        {
            Contract.Requires(question != null);
            Contract.Requires(submittedAnswers != null);

            this.Identifier = question.Identifier;

            this.Update(question, playerInitiatedUpdate: true); // Set all standard fields in one place

#if DEBUG
            // Make sure we're on the UI thread here, ListCollectionView requires it
            System.Diagnostics.Debug.Assert(System.Windows.Application.Current.Dispatcher.Thread.ManagedThreadId == System.Threading.Thread.CurrentThread.ManagedThreadId);
#endif

            this.Notes = notes;

            this.SubmittedAnswers = new TriviaObservableCollection<AnswerViewModel>(submittedAnswers.Select(answer => new AnswerViewModel(answer)));

            if (ContestConfiguration.Contest == ContestConfiguration.ContestType.HouseOfInsanity)
            {
                this.SubmittedUncalledAnswers = new ListCollectionView(this.SubmittedAnswers);
                this.SubmittedUncalledAnswers.Filter = obj => !((AnswerViewModel)obj).HasBeenCalledIn && !((AnswerViewModel)obj).WillCallInNext;
                this.SubmittedUncalledAnswers.SortDescriptions.Add(new SortDescription("Priority", ListSortDirection.Descending));
                this.SubmittedUncalledAnswers.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));

                this.PartialAnswers = new ListCollectionView(this.SubmittedAnswers);
                this.PartialAnswers.Filter = obj => ((AnswerViewModel)obj).Partial;
                this.PartialAnswers.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));

                this.AnswersToCallInNext = new ListCollectionView(this.SubmittedAnswers);
                this.AnswersToCallInNext.Filter = obj => !((AnswerViewModel)obj).HasBeenCalledIn && ((AnswerViewModel)obj).WillCallInNext;
                this.AnswersToCallInNext.SortDescriptions.Add(new SortDescription("Priority", ListSortDirection.Descending));
                this.AnswersToCallInNext.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));

                this.SubmittedAnswersForReadOnlyTab = new ListCollectionView(this.SubmittedAnswers);
                this.SubmittedAnswersForReadOnlyTab.SortDescriptions.Add(new SortDescription("Text", ListSortDirection.Ascending));  // alphabetically

                this.Researchers = new DispatchableObservableCollection<string>();

                this.HighlightAppearanceOfPartialsInAnswers();
            }
            else
            {
                
                // State 1 (Ruled out), State 2 (1 star), State 3 (2 stars)... State 6 (5 stars), State 7 (single (theoretically) answer to call in) 
                this.BeerpigRankedAnswers = new ListCollectionView[8];

                for (int i = 1; i <= 8; ++i)
                {
                    int tempLocal = i;  // capture for closure

                    ListCollectionView lcv = new ListCollectionView(this.SubmittedAnswers);
                    lcv.Filter = obj => ((AnswerViewModel)obj).State == tempLocal;
                    lcv.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));
                    this.BeerpigRankedAnswers[i - 1] = lcv;
                }
            }
        }
コード例 #2
0
ファイル: DataUpdateHandler.cs プロジェクト: bignis/Trivia
        public static void Start(ClientOrchestrator orchestrator, TriviaViewModel viewModel, Dispatcher dispatcher, TriviaClient window)
        {
            Contract.Requires(orchestrator != null);
            Contract.Requires(viewModel != null);

            orchestrator.AuthenticateResponseReceived +=
                delegate (AuthenticationResponse authResponse)
                {
                    // Since as of 2016, this response contains hour/player data, need to process this
                    if (authResponse.CurrentHourData != null)  // Will be null if authentication was unsuccessful
                    {
                        orchestrator.TriggerGetCompleteHourDataResponseReceivedEvent(authResponse.CurrentHourData);
                    }

                    if (authResponse.Players != null)
                    {
                        orchestrator.TriggerPlayersListUpdateReceivedEvent(authResponse.Players);
                    }

                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            // HourViewModel requires being on the UI thread
                            LoadGlobalDataIntoViewModel(authResponse.GlobalData, viewModel);
                        }));
                };
                      
            // Complete Hour
            orchestrator.GetCompleteHourDataResponseReceived +=
                delegate(CompleteHourData chd)
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            Debug.WriteLine("CompletHourDataResponse for hour " + chd.HourNumber.ToString());

                            HourViewModel hour = viewModel.GetHour(chd.HourNumber);

                            hour.IsNoLongerCurrentHour = chd.IsNoLongerCurrentHour;

                            hour.Questions.Clear();

                            hour.HasRetrievedQuestionsFromServer = true;  // Flag that we've got the latest & greatest

                            ILookup<int, Answer> answerLookup = chd.Answers.ToLookup(a => a.Number);
                            ILookup<int, Note> noteLookup = chd.Notes.ToLookup(n => n.QuestionNumber);

                            // Obviously this could be fancier w/ LINQ, but eh, good enough
                            foreach (Question questionModel in chd.Questions)
                            {
                                var answers = answerLookup[questionModel.Number];
                                var notes = new DispatchableObservableCollection<Note>(noteLookup[questionModel.Number]);

                                QuestionViewModel questionViewModel = new QuestionViewModel(questionModel, answers, notes);

                                hour.Questions.Add(questionViewModel);
                            }
                        }));
                };
            
            // Latest Hour (for informing the player that old hours are no longer active)
            orchestrator.GetLatestHourResponseReceived +=
                delegate(int latestHour)
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            foreach (HourViewModel hour in viewModel.Hours)
                            {
                                hour.IsNoLongerCurrentHour = hour.Number < latestHour;
                            }
                        }));
                };

            // Questions

            orchestrator.OpenQuestionResponseReceived += delegate (int hourNumber, int questionNumber, bool successful, string failureMessage)
            {
                // BeginInvoke is absolutely key here, otherwise the UI thread will be tied up and messages (such as Start Audio Trivia Recording) won't
                // be able to be processed as long as the UI thread is in use.  Regular old Invoke() will cause this problem.
                dispatcher.BeginInvoke(new Action(
                        delegate
                        {
                            if (!(successful))
                            {
                                MessageBox.Show(failureMessage, string.Format("Question #{0} Was Not Opened", questionNumber), MessageBoxButton.OK, MessageBoxImage.Exclamation);
                                return;  // Nothing to do
                            }

                            HourViewModel hour = viewModel.GetHour(hourNumber);

                            // Boy, I hope that the Update Question response (which creates the question) is received before this is...
                            QuestionViewModel question;

                            if (!(viewModel.TryGetQuestion(hourNumber, questionNumber, out question)))
                            {
                                // Fake it out since we didn't get a real one from the server
                                question = new QuestionViewModel(new Question(hourNumber, questionNumber));

                                hour.Questions.Add(question);
                            }

                            EditQuestionDialog dialog = new EditQuestionDialog(orchestrator, question, true);
                            dialog.Owner = window;

                            bool? result = dialog.ShowDialog();

                            if (!(result.HasValue) || result.Value == false)
                            {
                                return;  // Cancelled / Closed without saving
                            }

                            QuestionChanges changes = dialog.GetChangesMade();

                            if (changes.Changes == QuestionChanges.Change.None)
                            {
                                return;  // No changes actually were made even though the user clicked the Save button
                            }

                            orchestrator.SendUpdateQuestionRequest(changes);
                        }));
            };

            orchestrator.UpdateQuestionResponseReceived +=
                delegate(Question updatedQuestion, bool playerInitatedUpdate)
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            HourViewModel hour = viewModel.GetHour(updatedQuestion.Hour);

                            // If the question is just being opened, the first we hear of this question is the Update message

                            QuestionViewModel wrapperToUpdate;

                            if (!(viewModel.TryGetQuestion(updatedQuestion.Identifier, out wrapperToUpdate)))
                            {
                                // A brand new opened question
                                QuestionViewModel questionToAdd = new QuestionViewModel(updatedQuestion);

                                hour.Questions.Add(questionToAdd);
                                return;
                            }

                            ToastFactory.ShowAppropriateToast(viewModel.GetHour(updatedQuestion.Hour), wrapperToUpdate, updatedQuestion);

                            if (playerInitatedUpdate)  // If an automatic process updated this question (such as Predicted Duration change), a person could still be editing
                            {
                                wrapperToUpdate.PersonCurrentlyEditingThisQuestion = null;  // Flag that there's (likely) nobody else Editing this question
                            }

                            wrapperToUpdate.Update(updatedQuestion, playerInitatedUpdate);
                        }));
                };

            orchestrator.EditingQuestionMessageReceived +=
                delegate(EditingQuestionData data)
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            HourViewModel hour = viewModel.GetHour(data.HourNumber);

                            // If the question is just being opened, the first we hear of this question is the Update message
                            QuestionViewModel questionBeingUpdated;

                            if (!(viewModel.TryGetQuestion(data.HourNumber, data.QuestionNumber, out questionBeingUpdated)))
                            {
                                return;  // Odd that we can't find it, but this message isn't super-important so just move on
                            }

                            questionBeingUpdated.PersonCurrentlyEditingThisQuestion = data.WhoEditing;
                        }));
                };

            // Answers

            orchestrator.UpdateAnswerResponseReceived +=
                delegate(Answer answer, bool partialChanged)
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            //Debug.WriteLine(string.Format("UpdateAnswer.  Number:{0}.  IsPartial:{1}.  PartialChanged:{2}.  HasBeenCalledIn:{3}", answer.Number, answer.Partial, partialChanged, answer.HasBeenCalledIn));

                            QuestionViewModel question;

                            if (!(viewModel.TryGetQuestion(answer, out question)))
                            {
                                // If we can't find it - it's assumed to be for an hour that's not retrievd from the server yet.
                                return;  
                            }

                            if (partialChanged && answer.Partial)
                            {
                                // Toasty!
                                ToastFactory.ShowToastForPartial(answer);
                            }

                            AnswerViewModel storedAnswer = question.SubmittedAnswers.SingleOrDefault(a => a.ID == answer.ID);

                            if (storedAnswer != null)  // Wouldn't expect it to be null, but in 2014 it was null in the wild one random time
                            {
                                // Make the update
                                storedAnswer.Update(answer);
                            }

                            // If it changed partial status, or it's already a partial (and edited to change the text)
                            if (partialChanged || answer.Partial)
                            {
                                if (IsOneWordAnswer(answer.Text))
                                {
                                    // The fact that this answer changed partial status doesn't affect anything else, ignore
                                    // because we only care about one-word answers
                                    question.HighlightAppearanceOfPartialsInAnswers();
                                }
                            }
                        }));
                };

            orchestrator.SubmitAnswerResponseReceived +=
                delegate(Answer answer)
                {
                    //  viewModel.TryGetQuestion() eventually could trigger a new Hour to be created, which relies on the UI thread (Hours view)
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            QuestionViewModel question;

                            if (!(viewModel.TryGetQuestion(answer, out question)))
                            {
                                // If we can't find it - it's assumed to be for an hour that's not retrievd from the server yet.
                                return;
                            }

                            AnswerViewModel answerViewModel = new AnswerViewModel(answer);

                            question.SubmittedAnswers.Add(answerViewModel);

                            question.HighlightAppearanceOfPartialInThisAnswer(answerViewModel);

                            /*  // couldn't finish this in 2011.
                            // To keep track of how many of your personal answers are in the queue
                            viewModel.GetHour(question.Identifier.Hour).AddAnswerSubmittedByCurrentUser(answerViewModel);
                             */
                        }));
                };

            orchestrator.AddNoteMessageReceived +=
                delegate(Note note)
                {
                    QuestionViewModel question;

                    if (!(viewModel.TryGetQuestion(note.HourNumber, note.QuestionNumber, out question)))
                    {
                        // If we can't find it - it's assumed to be for an hour that's not retrievd rom the server yet.
                        return;
                    }

                    question.Notes.Add(note);
                };
            orchestrator.DeleteNoteMessageReceived +=
                delegate (int noteId)
                {
                    var allHoursStartingWithCurrent = new HourViewModel[] { viewModel.ActiveHour }.Concat(viewModel.Hours.Cast<HourViewModel>());
                    var allQuestionsStartingWithCurrentHour = allHoursStartingWithCurrent.SelectMany(hour => hour.Questions);
                    var firstMatchingNote = allQuestionsStartingWithCurrentHour.SelectMany(q => q.Notes).Where(note => note.ID == noteId).FirstOrDefault();

                    if (firstMatchingNote == null)
                    {
                        // Someone must have deleted a note that's not in the Client's cache of questions yet...
                        return;
                    }

                    QuestionViewModel question;

                    bool found = viewModel.TryGetQuestion(firstMatchingNote.HourNumber, firstMatchingNote.QuestionNumber, out question);

                    if (!(found))
                    {
                        return; // Question not currently loaded by the client yet?  No biggie, it'll get the latest values later when it is loaded
                    }

                    question.Notes.Remove(firstMatchingNote);
                };
            /* dead code?
            orchestrator.GetGlobalDataResponseReceived += (globalData) =>
                {
                    dispatcher.Invoke(new Action(
                        delegate
                        {
                            LoadGlobalDataIntoViewModel(globalData, viewModel);
                        }));
                };
                */
            orchestrator.CurrentCallerChangedMessageReceived += (currentCaller) =>
                {
                    viewModel.CurrentCaller = currentCaller;
                };

            orchestrator.ResearcherChangedResponseReceived += (changes) =>
            {
                dispatcher.Invoke(new Action(
                    delegate
                    {
                        Debug.WriteLine("ResearcherChanged response received");

                        // First, remove this researcher from any (if applicable) questions they were researching previously
                        var allHoursStartingWithCurrent = new HourViewModel[] { viewModel.ActiveHour }.Concat(viewModel.Hours.Cast<HourViewModel>());
                        var allQuestionsStartingWithCurrentHour = allHoursStartingWithCurrent.SelectMany(hour => hour.Questions);
                        var previouslyResearchedQuestion = allQuestionsStartingWithCurrentHour.Where(q => q.Researchers.Contains(changes.Name)).FirstOrDefault();

                        Debug.WriteLine(string.Format("PreviouslyRsearchedQuestion: {0}", previouslyResearchedQuestion == null ? "null" : previouslyResearchedQuestion.Identifier.Number.ToString()));

                        if (previouslyResearchedQuestion != null)
                        {
                            // They are no longer researching this question
                            previouslyResearchedQuestion.Researchers.Remove(changes.Name);

                            // If the "person" is the current user, need to uncheck the checkbox
                            if (changes.Name == TriviaClient.PlayerName)
                            {
                                previouslyResearchedQuestion.IsBeingResearched = false;
Debug.WriteLine(string.Format("Unchecking checkbox for: {0}", previouslyResearchedQuestion.Identifier.Number.ToString()));
                            }
                        }

                        QuestionViewModel question;

                        bool found = viewModel.TryGetQuestion(changes.HourNumber, changes.QuestionNumber, out question);

                        if (!(found))
                        {
                            return; // Question not currently loaded by the client yet?  No biggie, it'll get the latest values later when it is loaded
                        }

                        question.Researchers.Add(changes.Name);  // Add to end of list

                        Debug.WriteLine(string.Format("{0} added to researchers of question: {1}", changes.Name, question.Identifier.Number));

                    }), DispatcherPriority.Background); // ResearcherChanged tends to trigger some slow things, so make it a low priority since it's nonessential
            };

            // Links

            orchestrator.AddLinkResponseReceived += (link) =>
            {
                dispatcher.Invoke(new Action(
                    delegate
                    {
                        viewModel.Links.Add(link);
                    }));
            };

            // Players
            orchestrator.PlayersListUpdateReceived += (IEnumerable<Player> players) =>
            {
                dispatcher.Invoke(new Action(
                    delegate
                    {
                        
                        viewModel.ConnectedPlayers.Clear();
                        viewModel.RecentlyJoinedPlayers.Clear();
                        viewModel.RecentlyDepartedPlayers.Clear();

                        foreach (Player player in players)
                        {
                            if (player.RecentlyDeparted)
                            {
                                viewModel.RecentlyDepartedPlayers.Add(player);
                            }
                            else if (player.RecentlyJoined)
                            {
                                viewModel.RecentlyJoinedPlayers.Add(player);
                            }
                            else
                            {
                                viewModel.ConnectedPlayers.Add(player);
                            }
                        }
                    }), DispatcherPriority.Background);
            };
        }