Esempio n. 1
0
        private void HandleDesignTimeView()
        {
            HourViewModel hour = new HourViewModel(3);

            List<QuestionViewModel> qs = new List<QuestionViewModel>();

            QuestionViewModel q = new QuestionViewModel(
                new Question(69, 5)
                );
            q.Text = "Design: How much wood could a woodchuck chuck?";
            q.Points = 100;

            hour.Questions.Add(q);

            QuestionViewModel q2 = new QuestionViewModel(
                new Question(69, 6)
                );
            q2.Text = "Design: What's my name?";
            q2.Points = 50;

            hour.Questions.Add(q2);

            this.DataContext = qs;
        }
Esempio n. 2
0
        public static void ShowAppropriateToast(HourViewModel currentHour, QuestionViewModel oldQuestionState, Question currentQuestionState)
        {
            if (oldQuestionState.State == currentQuestionState.State)
            {
                Debug.WriteLine(string.Format("No toast displayed because oldQuestionState.{0} == currentQuestionState.{1}", oldQuestionState.State, currentQuestionState.State));
                return;  // If the state didn't change, don't show a toast
            }

            if (currentQuestionState.State == QuestionState.Open)
            {
                Debug.WriteLine(string.Format("No toast displayed because currentQuestionState.{0} == QuestionState.Open", currentQuestionState.State));
                return;  // no need to show toast
            }

            string imageKey;
            string text;
            ToastNotification.TriviaIndication indication;

            if (currentQuestionState.State == QuestionState.Wrong)
            {
                if (oldQuestionState.IsBeingResearched == false)
                {
                    Debug.WriteLine(string.Format("No toast displayed because oldQuestionState.IsBeingResearched({0}) == false", oldQuestionState.IsBeingResearched));
                    return;  // Don't show any toast if a question's closed that you weren't working on
                }

                // Just closed, didn't get an answer
                text = string.Format("Question #{0} Now Closed.", currentQuestionState.Identifier.Number);

                indication = ToastNotification.TriviaIndication.IncorrectQuestion;

                imageKey = GetImageForQuestion(currentQuestionState.Identifier.Hour, currentQuestionState.Identifier.Number, ToastFactory.WrongImages);

                // If it's a high-pointer, let's show a special meme instead
                if (currentQuestionState.Points >= 80)
                {
                    imageKey = GetImageForQuestion(currentQuestionState.Identifier.Hour, currentQuestionState.Identifier.Number, ToastFactory.HighPointWrongImages);
                }
            }
            else if (currentQuestionState.State == QuestionState.Correct)
            {
                // Always show toast for correct questions (maybe in the future we should have a threshold)

                text = string.Format("Got Correct Answer for #{0} - {1} points!", currentQuestionState.Identifier.Number, currentQuestionState.Points);
               
                indication = ToastNotification.TriviaIndication.CorrectQuestion;

                imageKey = GetImageForQuestion(currentQuestionState.Identifier.Hour, currentQuestionState.Identifier.Number, ToastFactory.CorrectImages);
            }
            else
            {
                throw new InvalidOperationException("Toast in wrong place unexpectedly");
            }

            ImageSource image = (ImageSource)ToastFactory.mImagesDictionary[imageKey];

            bool shouldShowStopResearching = oldQuestionState.IsBeingResearched;

            ToastNotification toast = new ToastNotification(image);
            toast.Indication = indication;
            toast.BodyText = text;

            toast.Show();

            // Extra toasts for special situations?

            if (currentQuestionState.Identifier.Number == 9 &&
                currentHour.OpenQuestions.Count == 0 &&
                currentHour.Questions.All(q => q.State == QuestionState.Correct))
            {
                ToastNotification allofthethings = new ToastNotification((ImageSource)ToastFactory.mImagesDictionary["allofthethings"], false);
                allofthethings.Indication = indication;
                allofthethings.BodyText = "Perfect Hour";

                allofthethings.Show();
            }

            // If it's a low pointer that we missed, add an extra toast meme (only show the people researching though to avoid spam)
            if (currentQuestionState.State == QuestionState.Wrong &&
                currentQuestionState.Points < 20)  // Less than 20 points we'll consider small
            {
                ToastNotification fuckitbillmurray = new ToastNotification((ImageSource)ToastFactory.mImagesDictionary["fuckitbillmurray"], false);
                fuckitbillmurray.Indication = indication;
                fuckitbillmurray.BodyText = "Small Question Missed";

                fuckitbillmurray.Show();
            }

            // Always last/leftmost
            if (shouldShowStopResearching)
            {
                ToastNotification stopResearchingToast = new ToastNotification((ImageSource)ToastFactory.mImagesDictionary["StopResearching"], false);
                stopResearchingToast.Indication = indication;
                stopResearchingToast.BodyText = "Stop Researching This Question";

                stopResearchingToast.Show();
            }
        }
Esempio n. 3
0
        private void RecalculateMaximumTotalPointsPossible(HourViewModel hourThatChanged)
        {
            int maxPointsPossible = this.mHours.AsParallel().Max(hour => hour.Stats.TotalPointsPossible);

            foreach (HourViewModel h in this.mHours)
            {
                h.Stats.MaximumTotalPointsPossibleInAnyHour = maxPointsPossible;
            }
        }
Esempio n. 4
0
        private void RecalculateTotalPointsPossible(HourViewModel hourThatChanged)
        {
            this.RecalculateMaximumTotalPointsPossible(hourThatChanged);  // Another highly related calculation

            this.TotalPointsPossible = this.mHours.AsParallel().Sum(hour => hour.Stats.TotalPointsPossible);
        }
Esempio n. 5
0
        private HourViewModel CreateNewHour(int hourNumber)
        {
            // If it's the first one in the dictionary
            if (this.mHourLookupByNumber.Count == 0)
            {
                this.OnFirstHourAdded(hourNumber);
            }

            // Make sure it's always in the dictionary
            HourViewModel hour = new HourViewModel(hourNumber);
            this.mHourLookupByNumber[hourNumber] = hour;
            this.mHours.Add(hour);

            this.NotifyPropertyChanged("Hours");

            // If enough questions get opened in an hour, make the next hour available
            hour.Questions.CollectionChanged += (sender, args) =>
                {
                    IEnumerable<QuestionViewModel> questions = (IEnumerable<QuestionViewModel>)sender;

                    if (questions.Count() >= ContestConfiguration.QuestionsThresholdBeforeAllowingNextHourToBeOpened && 
                        this.HourNumbersAvailable.Last() == questions.First().Identifier.Hour)  // Is the last hour the one that just changed?
                    {
                        this.HourNumbersAvailable.Add(hourNumber + 1);
                    }
                };
            
            // Track the biggest hour's max points possible
            hour.Stats.PropertyChanged += (sender, args) =>
                {
                    if (string.Equals(args.PropertyName, "TotalPointsPossible", StringComparison.Ordinal))
                    {
                        this.RecalculateTotalPointsPossible(hour);
                        return;
                    }

                    if (string.Equals(args.PropertyName, "TotalPointsEarned", StringComparison.Ordinal))
                    {
                        this.RecalculateTotalPointsEarned();
                        return;
                    }   
                };

            return hour;
        }
Esempio n. 6
0
        public void SetActiveHourNumber(int hourNumber)
        {
            Contract.Ensures(this.ActiveHour.Number == hourNumber);

            HourViewModel hour = this.GetHour(hourNumber);

            bool needNotifyActiveHourChanged = (this.ActiveHour == null || this.ActiveHour.Number != hourNumber);

            // Change the value
            this.ActiveHour = hour;

            if (needNotifyActiveHourChanged)
            {
                this.NotifyPropertyChanged("ActiveHour");
            }
        }
Esempio n. 7
0
        private void TestMemes(object sender, RoutedEventArgs e)
        {

            /* perfect hour
            HourViewModel hvm = new HourViewModel(1);
            hvm.Questions.Add(new QuestionViewModel(new Question(4, 8)) { State = QuestionState.Open });
            hvm.Questions.Add(new QuestionViewModel(new Question(4, 9)) { State = QuestionState.Correct, IsBeingResearched = true });
            ToastFactory.ShowAppropriateToast(hvm, hvm.Questions[1], hvm.Questions[1]);
            */
            /* missed small pointer
            HourViewModel hvm = new HourViewModel(1);
            hvm.Questions.Add(new QuestionViewModel(new Question(4, 9)) { State = QuestionState.Open });
            hvm.Questions.Add(new QuestionViewModel(new Question(4, 9)) { State = QuestionState.Wrong, IsBeingResearched = true, Points = 5 });
            ToastFactory.ShowAppropriateToast(hvm, hvm.Questions[1], hvm.Questions[1]);
            */

            // missed big pointer
            HourViewModel hvm = new HourViewModel(1);
            hvm.Questions.Add(new QuestionViewModel(new Question(4, 9)) { State = QuestionState.Open, IsBeingResearched = true, Points = 125 });
            Question current = new Question(4, 9) { State = QuestionState.Wrong, Points = 125 };
            ToastFactory.ShowAppropriateToast(hvm, hvm.Questions.Single(), current);
        }
Esempio n. 8
0
        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);
            };
        }