/// <summary> /// Create the new slides and load them up. /// </summary> /// <param name="n"></param> /// <param name="pdfFile"></param> /// <param name="fullVM"></param> /// <returns></returns> private async Task <SlideThumbViewModel[]> CreateNewThumbs(int n, PDFFile pdfFile, Lazy <FullTalkAsStripViewModel> fullVM) { var newSlides = Enumerable.Range(SlideThumbnails.Count, n - SlideThumbnails.Count).Select(i => new SlideThumbViewModel(pdfFile.GetPageStreamAndCacheInfo(i), fullVM, i)).ToArray(); foreach (var sld in newSlides) { await sld.LoadSize(); } return(newSlides); }
/// <summary> /// Get everything setup to show the PDF document /// </summary> /// <param name="docSequence"></param> /// <param name="initialPage">The page that should be shown when we start up. Zero indexed</param> /// <param name="screen">The screen that hosts everything (routing!)</param> public FullTalkAsStripViewModel(IScreen screen, PDFFile file) { Debug.Assert(file != null); Debug.Assert(screen != null); HostScreen = screen; // We basically re-set each time a new file comes down from the top. Pages = new ReactiveList <PDFPageViewModel>(); var pageSizeChanged = file.WhenAny(x => x.NumberOfPages, x => x.Value) .DistinctUntilChanged() .ObserveOn(RxApp.MainThreadScheduler); var b = from newPageLength in pageSizeChanged let np = Pages.Count from allpages in CreateNPages(newPageLength - np, np, file) select new { numPages = newPageLength, freshPages = allpages }; b .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(info => SetNPages(info.numPages, info.freshPages)); // Page navigation. Make sure things are clean and we don't over-burden the UI before // we pass the info back to the UI! _moveToPage = new ReplaySubject <int>(1); _loaded = new ReplaySubject <Unit>(1); MoveToPage = _moveToPage .CombineLatest(_loaded, (p, _) => p) .Select(scrubPageIndex) .DistinctUntilChanged(); PageForward = ReactiveCommand.Create(); PageForward .Cast <int>() .Select(pn => pn + 1) .Subscribe(_moveToPage); PageBack = ReactiveCommand.Create(); PageBack .Cast <int>() .Select(pn => pn - 1) .Subscribe(_moveToPage); PageMove = ReactiveCommand.Create(); PageMove .Cast <int>() .Subscribe(_moveToPage); }
/// <summary> /// Create n pages, as requested, and prepare them for the UI. /// </summary> /// <param name="n"></param> /// <param name="file"></param> /// <returns></returns> private async Task <PDFPageViewModel[]> CreateNPages(int n, int start_index, PDFFile file) { if (n <= 0) { return(new PDFPageViewModel[0]); } // Create and initialize pages var newPages = Enumerable.Range(start_index, n).Select(i => new PDFPageViewModel(file.GetPageStreamAndCacheInfo(i))).ToArray(); var sequenceOfPages = newPages.Select(p => p.LoadSize()); foreach (var seq in sequenceOfPages) { var r = await seq.ToArray(); } return(newPages); }
/// <summary> /// Setup the VM for the file. /// </summary> /// <param name="downloader">The download controller. This assumes this is for a PDF file, and it is Valid.</param> /// <param name="talkTime">The time that this guy is relevant</param> public FileSlideListViewModel(PDFFile pdfFile, TimePeriod talkTime) { // Get the object consistent. SlideThumbnails = new ReactiveList <SlideThumbViewModel>(); // We will want to refresh the view of this file depending on how close we are to the actual // meeting time. var innerBuffer = new TimePeriod(talkTime); innerBuffer.StartTime -= TimeSpan.FromMinutes(30); innerBuffer.EndTime += TimeSpan.FromHours(2); var updateTalkFile = Observable.Empty <Unit>(); if (innerBuffer.Contains(DateTime.Now)) { // Fire every 15 minutes, but only while in the proper time. // We only check when requested, so we will start right off the bat. updateTalkFile = Observable.Return(default(Unit)) .Concat(Observable.Interval(TimeSpan.FromMinutes(15)) .Where(_ => innerBuffer.Contains(DateTime.Now)) .Select(_ => default(Unit)) ) .Where(_ => Settings.AutoDownloadNewMeeting); } // A view model to show the whole thing as a strip view. var fullVM = new Lazy <FullTalkAsStripViewModel>(() => new FullTalkAsStripViewModel(Locator.Current.GetService <IScreen>(), pdfFile)); // All we do is sit and watch for the # of pages to change, and when it does, we fix up the list of SlideThumbViewModel. var newSlideInfo = from nPages in pdfFile.WhenAny(x => x.NumberOfPages, x => x.Value).DistinctUntilChanged() from newSlides in CreateNewThumbs(nPages, pdfFile, fullVM) select new { n = nPages, slides = newSlides }; newSlideInfo .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(info => SetNumberOfThumbNails(info.n, info.slides)); }
/// <summary> /// Setup the links to run. /// </summary> public ExpandingSlideThumbViewModel(PDFFile downloader, TimePeriod talkTime) { // Showing the slides should generate it here, and nullify it everywhere else. ShowSlides = ReactiveCommand.Create(); // FIre the reset command, with the current downloader as an argument ShowSlides .Select(_ => downloader) .InvokeCommand(_resetSlideShow); // When the reset shows up, only pay attention if a different file is being shown // and we have something to show! var noThumbs = _resetSlideShow .Where(dl => downloader != dl && downloader.NumberOfPages != 0) .Select(_ => (FileSlideListViewModel)null); // When we want to show the slides just create the new view model. var newThumbs = ShowSlides .Select(_ => new FileSlideListViewModel(downloader, talkTime)); // Now, allow the property we manage to flip back and forth here. Observable.Merge(newThumbs, noThumbs) .WriteLine(x => string.Format("--> writing out a {0} to the actual display property.", x == null ? "null" : "non-null")) .ToProperty(this, x => x.TalkAsThumbs, out _talkAsThumbs, null, RxApp.MainThreadScheduler); // The logic for canShowThumbs is tricky: // 1. True if the downloader has downloaded and opened the file and parsed it successfully (# pages != 0). // 2. False if we have fired off a new thumbs guy, but that can be only active if can show thumbs is true (right??). // 3. True if we've shown thumbs and we stop showing them. var downloaded = downloader.WhenAny(x => x.NumberOfPages, x => x.Value != 0); var areShowing = Observable.Merge(newThumbs, noThumbs) .Select(x => x == null ? true : false); Observable.Merge(downloaded, areShowing) .WriteLine(x => string.Format(" -> and writing out {0} for can show thumbs", x)) .ToProperty(this, x => x.CanShowThumbs, out _canShowThumbs, false, RxApp.MainThreadScheduler); // Track the # of pages. Used to display some info below the button in most implementations. downloader.WhenAny(x => x.NumberOfPages, x => x.Value) .ToProperty(this, x => x.NumberOfSlides, out _numberOfSlides, 0, RxApp.MainThreadScheduler); }
/// <summary> /// Configure for showing multiple files. /// </summary> /// <param name="files"></param> public TalkFileCollectionUserControlViewModel(IFile[] files, ITalk t) { // The title we use is what we grab from the first file. Title = files.Length > 0 ? files[0].DisplayName : ""; // Show the list of files that can downloaded/opened. These guys can opened by other // apps in the system by clicking or pressing on them. var allFilesVM = (from f in files select new { FilePointer = f, UserControl = new FileUserControlViewModel(f) }) .ToArray(); TalkFiles = new ReactiveList <FileUserControlViewModel>(); TalkFiles.AddRange(allFilesVM.Select(f => f.UserControl)); #if WINDOWS_APP // If there is a PDF file, then we use that to show a "hero" slide. // TODO: WARNING - this will create a PDFFile, but one may not want that here // if one is also going to create other PDF file guys!! var pdf = allFilesVM.Where(f => f.FilePointer.FileType == "pdf" && f.FilePointer.IsValid).FirstOrDefault(); if (pdf != null) { var pdfFile = new PDFFile(pdf.UserControl.FileDownloader); var fullVM = new Lazy <FullTalkAsStripViewModel>(() => new FullTalkAsStripViewModel(Locator.Current.GetService <IScreen>(), pdfFile)); HeroSlide = new FirstSlideHeroViewModel(pdfFile, fullVM); var timeSpan = new TimePeriod(t.StartTime, t.EndTime); Thumbs = new ExpandingSlideThumbViewModel(pdfFile, timeSpan); } else { HeroSlide = new FirstSlideHeroViewModel((PDFFile)null, null); } #endif }
/// <summary> /// Create the VM. We generate a first page hero slide. /// </summary> /// <param name="file">The PDF File to generate. If null, we will make this VM as invalid</param> public FirstSlideHeroViewModel(PDFFile file, Lazy <FullTalkAsStripViewModel> fullVM) { // If we are actually connected to a file, then // - setup the hero slide // - a button to show all slides _heroPageUC = null; HaveHeroSlide = false; if (file != null) { // Hero slide. Tricky because we can't display until // a fetch has been done on the PDF data. var pdf = new PDFPageViewModel(file.GetPageStreamAndCacheInfo(0)); _heroPageUC = pdf.LoadSize() .Select(_ => pdf) .ToProperty(this, m => m.HeroPageUC, scheduler: RxApp.MainThreadScheduler); HaveHeroSlide = true; // Allow a full view OpenFullView = ReactiveCommand.Create(); OpenFullView .Subscribe(_ => fullVM.Value.LoadPage(0)); } }