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(); } }
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; } } }
public EditQuestionDialog(ClientOrchestrator orchestrator, QuestionViewModel question, bool isOpeningQuestion) : this() { Contract.Requires(orchestrator != null); Contract.Requires(question != null); this.mQuestion = question; this.mSnapshotOfQuestionPriorToEdits = question.CreateModel(); this.mViewModel = new EditQuestionViewModel(question, isOpeningQuestion); this.mOrchestrator = orchestrator; this.mOrchestrator.StartAudioTriviaRecordingResponseReceived += new ClientOrchestrator.StartAudioTriviaRecordingEventHandler(mOrchestrator_StartAudioTriviaRecordingResponseReceived); this.mOrchestrator.StopAudioTriviaRecordingResponseReceived += new ClientOrchestrator.StopAudioTriviaRecordingEventHandler(mOrchestrator_StopAudioTriviaRecordingResponseReceived); this.DataContext = this.mViewModel; this.mQuestionTextTextbox.Text = question.Text; // Because we don't want other people's updates to change the textbox, binding is OneWayToSource, so we need to set the initial value // If it starts with the magic text, manually set this flag isOpeningQuestion |= (question.Text != null && question.Text.StartsWith("Currently being opened")); // Set up bindings if (isOpeningQuestion) { // We can do this since IsOpeningQuestion will not change this.mAnswerPanel.Visibility = Visibility.Collapsed; this.mPhoneBankPanel.Visibility = Visibility.Collapsed; this.mStatePanel.Visibility = Visibility.Collapsed; question.Text = string.Empty; // Start with empty text if opening the question // Special labels for opening this.Title = "Open Question"; this.mHeaderActionLabel.Text = "Open Question"; this.mRecordAudioTriviaButton.Focus(); this.mRecordAudioTriviaButton.Click += new RoutedEventHandler(this.OnRecordingAudioTriviaClickedWhileOpeningQuestion); // Hack, because it's bound to a question RoutedEventHandler focusHandler = null; focusHandler = (sender, e) => { this.mQuestionTextTextbox.Clear(); this.mQuestionTextTextbox.GotFocus -= focusHandler; // only once! }; this.mQuestionTextTextbox.GotFocus += focusHandler; } else { this.mAnswerPanel.SetBinding(Panel.VisibilityProperty, new ConvertingBinding<QuestionState, Visibility>("Question.State", state => state == QuestionState.Open ? Visibility.Collapsed : Visibility.Visible)); this.mPhoneBankPanel.SetBinding(Panel.VisibilityProperty, new ConvertingBinding<QuestionState, Visibility>("Question.State", state => state == QuestionState.Correct ? Visibility.Visible : Visibility.Collapsed)); this.mQuestionTextTextbox.Focus(); } // Set the text for the Record button this.mRecordThisQuestionButtonText.SetBinding(TextBlock.TextProperty, new ConvertingBinding<string, string>("Question.AudioTriviaFilename", audioTriviaFileName => string.IsNullOrWhiteSpace(audioTriviaFileName) ? "Record This Question" : "Re-Record This Question")); }
public QuestionViewModel(Question Question) : this(Question, new List<Answer>(), new DispatchableObservableCollection<Note>()) { }
public Question CreateModel() { Question model = new Question(this.Identifier.Hour, this.Identifier.Number); model.Text = this.Text; model.Answer = this.Answer; model.AudioTriviaFilename = this.AudioTriviaFilename; model.State = this.State; model.Points = this.Points; model.PhoneBankOperator = this.PhoneBankOperator; return model; }
public void Update(Question updatedQuestion, bool playerInitiatedUpdate) { // Identifier should never change... // SubmittedAnswers shouldn't change as a result of an update this.Text = updatedQuestion.Text; this.PredictedRemainingOpenDurationInMinutes = updatedQuestion.PredictedRemainingOpenDurationInMinutes; if (!(playerInitiatedUpdate)) { // Only things that should change as a result of an automatic update are Text and Predicted Duration return; } this.Answer = updatedQuestion.Answer; this.AudioTriviaFilename = updatedQuestion.AudioTriviaFilename; this.State = updatedQuestion.State; this.Points = updatedQuestion.Points; this.PhoneBankOperator = updatedQuestion.PhoneBankOperator; }
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); }
private static Question GetQuestionFromDataReader(SqlDataReader reader) { int hour = int.Parse(reader["qhour"].ToString()); int number = int.Parse(reader["qnumber"].ToString()); Question q = new Question(hour, number); q.Text = reader["qtext"].ToString(); q.Open = bool.Parse(reader["qopen"].ToString()); q.Correct = bool.Parse(reader["correct"].ToString()); q.Answer = reader["answer"].ToString(); q.Points = int.Parse(reader["points"].ToString()); q.AudioTriviaFilename = reader["audiotriviafilename"].ToString(); q.PhoneBankOperator = reader["phoneop"].ToString(); // Apply the Duration business rules... (this code is always assured to be running on the server (with "Server Time")) object predictedDurationObj = reader["predictedduration"]; object timeOpenedObj = reader["timeopened"]; // ex: 2011-01-17 19:56:00 if (predictedDurationObj != System.DBNull.Value && timeOpenedObj != System.DBNull.Value) { int predictedDurationInMinutes = int.Parse(predictedDurationObj.ToString()); DateTime timeOpened = (DateTime)timeOpenedObj; TimeSpan timeKeptOpenSoFar = DateTime.Now - timeOpened; /* per documentation, smalldatetime in SQL rounds to the nearest minute. Occasionally the opened time will appear to be "in the future", but it's really a rounding error. if (timeOpened > DateTime.Now) { throw new InvalidOperationException(string.Format("Wow, somehow the question was opened at {0} which is after Now={1}. Quite illegal.", timeOpened.ToLongTimeString(), DateTime.Now.ToLongTimeString())); } */ if (timeKeptOpenSoFar < TimeSpan.Zero) { timeKeptOpenSoFar = TimeSpan.Zero; // See above, prevent rounding error from making the UI weird. } q.PredictedRemainingOpenDurationInMinutes = predictedDurationInMinutes - (int)timeKeptOpenSoFar.TotalMinutes; } return q; }