Example #1
0
        public CloudViewModel(
            CloudState state,
            CreateFolderViewModelFactory createFolderFactory,
            RenameFileViewModelFactory renameFactory,
            FileViewModelFactory fileFactory,
            FolderViewModelFactory folderFactory,
            IAuthViewModel auth,
            IFileManager files,
            ICloud cloud)
        {
            _cloud = cloud;
            Folder = createFolderFactory(this);
            Rename = renameFactory(this);
            Auth   = auth;

            var canInteract = this
                              .WhenAnyValue(
                x => x.Folder.IsVisible,
                x => x.Rename.IsVisible,
                (folder, rename) => !folder && !rename);

            _canInteract = canInteract
                           .ToProperty(this, x => x.CanInteract);

            var canRefresh = this
                             .WhenAnyValue(
                x => x.Folder.IsVisible,
                x => x.Rename.IsVisible,
                x => x.Auth.IsAuthenticated,
                (folder, rename, authenticated) => !folder && !rename && authenticated);

            Refresh = ReactiveCommand.CreateFromTask(
                () => cloud.GetFiles(CurrentPath),
                canRefresh);

            _files = Refresh
                     .Select(
                items => items
                .Select(file => fileFactory(file, this))
                .OrderByDescending(file => file.IsFolder)
                .ThenBy(file => file.Name)
                .ToList())
                     .Where(items => Files == null || !items.SequenceEqual(Files))
                     .ToProperty(this, x => x.Files);

            _isLoading = Refresh
                         .IsExecuting
                         .ToProperty(this, x => x.IsLoading);

            _isReady = Refresh
                       .IsExecuting
                       .Skip(1)
                       .Select(executing => !executing)
                       .ToProperty(this, x => x.IsReady);

            var canOpenCurrentPath = this
                                     .WhenAnyValue(x => x.SelectedFile)
                                     .Select(file => file != null && file.IsFolder)
                                     .CombineLatest(Refresh.IsExecuting, canInteract, (folder, busy, ci) => folder && ci && !busy);

            Open = ReactiveCommand.Create(
                () => Path.Combine(CurrentPath, SelectedFile.Name),
                canOpenCurrentPath);

            var canCurrentPathGoBack = this
                                       .WhenAnyValue(x => x.CurrentPath)
                                       .Where(path => path != null)
                                       .Select(path => path.Length > cloud.InitialPath.Length)
                                       .CombineLatest(Refresh.IsExecuting, canInteract, (valid, busy, ci) => valid && ci && !busy);

            Back = ReactiveCommand.Create(
                () => Path.GetDirectoryName(CurrentPath),
                canCurrentPathGoBack);

            SetPath = ReactiveCommand.Create <string, string>(path => path);

            _currentPath = Open
                           .Merge(Back)
                           .Merge(SetPath)
                           .Select(path => path ?? cloud.InitialPath)
                           .DistinctUntilChanged()
                           .Log(this, $"Current path changed in {cloud.Name}")
                           .ToProperty(this, x => x.CurrentPath, state.CurrentPath ?? cloud.InitialPath);

            var getBreadCrumbs = ReactiveCommand.CreateFromTask(
                () => cloud.GetBreadCrumbs(CurrentPath));

            _breadCrumbs = getBreadCrumbs
                           .Where(items => items != null && items.Any())
                           .Select(items => items.Select(folder => folderFactory(folder, this)))
                           .ToProperty(this, x => x.BreadCrumbs);

            _showBreadCrumbs = getBreadCrumbs
                               .ThrownExceptions
                               .Select(exception => false)
                               .Merge(getBreadCrumbs.Select(items => items != null && items.Any()))
                               .ObserveOn(RxApp.MainThreadScheduler)
                               .ToProperty(this, x => x.ShowBreadCrumbs);

            _hideBreadCrumbs = this
                               .WhenAnyValue(x => x.ShowBreadCrumbs)
                               .Select(show => !show)
                               .ToProperty(this, x => x.HideBreadCrumbs);

            this.WhenAnyValue(x => x.CurrentPath, x => x.IsReady)
            .Where(x => x.Item1 != null && x.Item2)
            .Select(_ => Unit.Default)
            .InvokeCommand(getBreadCrumbs);

            this.WhenAnyValue(x => x.CurrentPath)
            .Skip(1)
            .Select(_ => Unit.Default)
            .InvokeCommand(Refresh);

            this.WhenAnyValue(x => x.CurrentPath)
            .Subscribe(_ => SelectedFile = null);

            _isCurrentPathEmpty = this
                                  .WhenAnyValue(x => x.Files)
                                  .Skip(1)
                                  .Where(items => items != null)
                                  .Select(items => !items.Any())
                                  .ToProperty(this, x => x.IsCurrentPathEmpty);

            _hasErrorMessage = Refresh
                               .ThrownExceptions
                               .Select(exception => true)
                               .ObserveOn(RxApp.MainThreadScheduler)
                               .Merge(Refresh.Select(x => false))
                               .ToProperty(this, x => x.HasErrorMessage);

            var canUploadToCurrentPath = this
                                         .WhenAnyValue(x => x.CurrentPath)
                                         .Select(path => path != null)
                                         .CombineLatest(Refresh.IsExecuting, canInteract, (up, loading, can) => up && can && !loading);

            UploadToCurrentPath = ReactiveCommand.CreateFromObservable(
                () => Observable
                .FromAsync(files.OpenRead)
                .Where(response => response.Name != null && response.Stream != null)
                .Select(args => _cloud.UploadFile(CurrentPath, args.Stream, args.Name))
                .SelectMany(task => task.ToObservable()),
                canUploadToCurrentPath);

            UploadToCurrentPath.InvokeCommand(Refresh);

            var canDownloadSelectedFile = this
                                          .WhenAnyValue(x => x.SelectedFile)
                                          .Select(file => file != null && !file.IsFolder)
                                          .CombineLatest(Refresh.IsExecuting, canInteract, (down, loading, can) => down && !loading && can);

            DownloadSelectedFile = ReactiveCommand.CreateFromObservable(
                () => Observable
                .FromAsync(() => files.OpenWrite(SelectedFile.Name))
                .Where(stream => stream != null)
                .Select(stream => _cloud.DownloadFile(SelectedFile.Path, stream))
                .SelectMany(task => task.ToObservable()),
                canDownloadSelectedFile);

            var canLogout = cloud
                            .IsAuthorized
                            .DistinctUntilChanged()
                            .Select(loggedIn => loggedIn && (
                                        cloud.SupportsDirectAuth ||
                                        cloud.SupportsOAuth ||
                                        cloud.SupportsHostAuth))
                            .CombineLatest(canInteract, (logout, interact) => logout && interact)
                            .ObserveOn(RxApp.MainThreadScheduler);

            Logout = ReactiveCommand.CreateFromTask(cloud.Logout, canLogout);

            _canLogout = canLogout
                         .ToProperty(this, x => x.CanLogout);

            var canDeleteSelection = this
                                     .WhenAnyValue(x => x.SelectedFile)
                                     .Select(file => file != null && !file.IsFolder)
                                     .CombineLatest(Refresh.IsExecuting, canInteract, (del, loading, ci) => del && !loading && ci);

            DeleteSelectedFile = ReactiveCommand.CreateFromTask(
                () => cloud.Delete(SelectedFile.Path, SelectedFile.IsFolder),
                canDeleteSelection);

            DeleteSelectedFile.InvokeCommand(Refresh);

            var canUnselectFile = this
                                  .WhenAnyValue(x => x.SelectedFile)
                                  .Select(selection => selection != null)
                                  .CombineLatest(Refresh.IsExecuting, canInteract, (sel, loading, ci) => sel && !loading && ci);

            UnselectFile = ReactiveCommand.Create(
                () => { SelectedFile = null; },
                canUnselectFile);

            UploadToCurrentPath.ThrownExceptions
            .Merge(DeleteSelectedFile.ThrownExceptions)
            .Merge(DownloadSelectedFile.ThrownExceptions)
            .Merge(Refresh.ThrownExceptions)
            .Merge(getBreadCrumbs.ThrownExceptions)
            .Log(this, $"Exception occured in provider {cloud.Name}")
            .Subscribe();

            this.WhenAnyValue(x => x.CurrentPath)
            .Subscribe(path => state.CurrentPath = path);

            this.WhenAnyValue(x => x.Auth.IsAuthenticated)
            .Select(authenticated => authenticated ? _cloud.Parameters?.Token : null)
            .Subscribe(token => state.Token = token);

            this.WhenAnyValue(x => x.Auth.IsAuthenticated)
            .Select(authenticated => authenticated ? _cloud.Parameters?.User : null)
            .Subscribe(user => state.User = user);

            this.WhenActivated(ActivateAutoRefresh);
        }
Example #2
0
        public ProviderViewModel(
            CreateFolderViewModelFactory createFolder,
            RenameFileViewModelFactory createRename,
            FileViewModelFactory createFile,
            IAuthViewModel authViewModel,
            IFileManager fileManager,
            IProvider provider,
            IScheduler current,
            IScheduler main)
        {
            _provider = provider;
            Folder    = createFolder(this);
            Rename    = createRename(this);

            var canInteract = this
                              .WhenAnyValue(
                x => x.Folder.IsVisible,
                x => x.Rename.IsVisible,
                (folder, rename) => !folder && !rename);

            _canInteract = canInteract
                           .DistinctUntilChanged()
                           .ToProperty(this, x => x.CanInteract, scheduler: current);

            _refresh = ReactiveCommand.CreateFromTask(
                () => provider.Get(CurrentPath),
                canInteract, main);

            _files = _refresh
                     .Select(files => files
                             .Select(file => createFile(file, this))
                             .OrderByDescending(file => file.IsFolder)
                             .ThenBy(file => file.Name)
                             .ToList())
                     .StartWithEmpty()
                     .Where(files => Files == null ||
                            files.Count != Files.Count() ||
                            !files.All(x => Files.Any(y => x.Path == y.Path &&
                                                      x.Modified == y.Modified)))
                     .ToProperty(this, x => x.Files, scheduler: current);

            _isLoading = _refresh
                         .IsExecuting
                         .ToProperty(this, x => x.IsLoading, scheduler: current);

            _isReady = _refresh
                       .IsExecuting
                       .Select(executing => !executing)
                       .Skip(1)
                       .ToProperty(this, x => x.IsReady, scheduler: current);

            var canOpenCurrentPath = this
                                     .WhenAnyValue(x => x.SelectedFile)
                                     .Select(file => file != null && file.IsFolder)
                                     .CombineLatest(_refresh.IsExecuting, (folder, busy) => folder && !busy)
                                     .CombineLatest(canInteract, (open, interact) => open && interact);

            _open = ReactiveCommand.Create(
                () => Path.Combine(CurrentPath, SelectedFile.Name),
                canOpenCurrentPath, main);

            var canCurrentPathGoBack = this
                                       .WhenAnyValue(x => x.CurrentPath)
                                       .Select(path => path.Length > provider.InitialPath.Length)
                                       .CombineLatest(_refresh.IsExecuting, (valid, busy) => valid && !busy)
                                       .CombineLatest(canInteract, (back, interact) => back && interact);

            _back = ReactiveCommand.Create(
                () => Path.GetDirectoryName(CurrentPath),
                canCurrentPathGoBack, main);

            _currentPath = _open
                           .Merge(_back)
                           .DistinctUntilChanged()
                           .Log(this, $"Current path changed in {provider.Name}")
                           .ToProperty(this, x => x.CurrentPath, provider.InitialPath, scheduler: current);

            this.WhenAnyValue(x => x.CurrentPath)
            .Skip(1)
            .Select(path => Unit.Default)
            .InvokeCommand(_refresh);

            this.WhenAnyValue(x => x.CurrentPath)
            .Subscribe(path => SelectedFile = null);

            _isCurrentPathEmpty = this
                                  .WhenAnyValue(x => x.Files)
                                  .Skip(1)
                                  .Where(files => files != null)
                                  .Select(files => !files.Any())
                                  .ToProperty(this, x => x.IsCurrentPathEmpty, scheduler: current);

            _hasErrors = _refresh
                         .ThrownExceptions
                         .Select(exception => true)
                         .Merge(_refresh.Select(x => false))
                         .ToProperty(this, x => x.HasErrors, scheduler: current);

            var canUploadToCurrentPath = this
                                         .WhenAnyValue(x => x.CurrentPath)
                                         .Select(path => path != null)
                                         .CombineLatest(_refresh.IsExecuting, (up, loading) => up && !loading)
                                         .CombineLatest(canInteract, (upload, interact) => upload && interact);

            _uploadToCurrentPath = ReactiveCommand.CreateFromObservable(
                () => Observable
                .FromAsync(fileManager.OpenRead)
                .Where(response => response.Name != null && response.Stream != null)
                .Select(x => _provider.UploadFile(CurrentPath, x.Stream, x.Name))
                .SelectMany(task => task.ToObservable()),
                canUploadToCurrentPath,
                main);

            _uploadToCurrentPath.InvokeCommand(_refresh);

            var canDownloadSelectedFile = this
                                          .WhenAnyValue(x => x.SelectedFile)
                                          .Select(file => file != null && !file.IsFolder)
                                          .CombineLatest(_refresh.IsExecuting, (down, loading) => down && !loading)
                                          .CombineLatest(canInteract, (download, interact) => download && interact);

            _downloadSelectedFile = ReactiveCommand.CreateFromObservable(
                () => Observable
                .FromAsync(() => fileManager.OpenWrite(SelectedFile.Name))
                .Where(stream => stream != null)
                .Select(stream => _provider.DownloadFile(SelectedFile.Path, stream))
                .SelectMany(task => task.ToObservable()),
                canDownloadSelectedFile,
                main);

            var isAuthEnabled = provider.SupportsDirectAuth || provider.SupportsOAuth;
            var canLogout     = provider
                                .IsAuthorized
                                .Select(loggedIn => loggedIn && isAuthEnabled)
                                .DistinctUntilChanged()
                                .CombineLatest(canInteract, (logout, interact) => logout && interact)
                                .ObserveOn(main);

            _logout    = ReactiveCommand.CreateFromTask(provider.Logout, canLogout);
            _canLogout = canLogout
                         .ToProperty(this, x => x.CanLogout, scheduler: current);

            var canDeleteSelection = this
                                     .WhenAnyValue(x => x.SelectedFile)
                                     .Select(file => file != null && !file.IsFolder)
                                     .CombineLatest(_refresh.IsExecuting, (del, loading) => del && !loading)
                                     .CombineLatest(canInteract, (delete, interact) => delete && interact);

            _deleteSelectedFile = ReactiveCommand.CreateFromTask(
                () => provider.Delete(SelectedFile.Path, SelectedFile.IsFolder),
                canDeleteSelection);

            _deleteSelectedFile.InvokeCommand(Refresh);

            var canUnselectFile = this
                                  .WhenAnyValue(x => x.SelectedFile)
                                  .Select(selection => selection != null)
                                  .CombineLatest(_refresh.IsExecuting, (sel, loading) => sel && !loading)
                                  .CombineLatest(canInteract, (unselect, interact) => unselect && interact);

            _unselectFile = ReactiveCommand.Create(
                () => { SelectedFile = null; },
                canUnselectFile);

            _uploadToCurrentPath
            .ThrownExceptions
            .Merge(_deleteSelectedFile.ThrownExceptions)
            .Merge(_downloadSelectedFile.ThrownExceptions)
            .Merge(_refresh.ThrownExceptions)
            .Log(this, $"Exception occured in provider {provider.Name}")
            .Subscribe();

            Auth      = authViewModel;
            Activator = new ViewModelActivator();
            this.WhenActivated(disposable =>
            {
                this.WhenAnyValue(x => x.Auth.IsAuthenticated)
                .Where(authenticated => authenticated)
                .Select(ignore => Unit.Default)
                .InvokeCommand(_refresh)
                .DisposeWith(disposable);

                var interval = TimeSpan.FromSeconds(1);
                Observable.Timer(interval, interval)
                .Select(unit => RefreshingIn - 1)
                .Where(value => value >= 0)
                .ObserveOn(main)
                .Subscribe(x => RefreshingIn = x)
                .DisposeWith(disposable);

                this.WhenAnyValue(x => x.RefreshingIn)
                .Skip(1)
                .Where(refreshing => refreshing == 0)
                .Log(this, $"Refreshing provider {provider.Name} path {CurrentPath}")
                .Select(value => Unit.Default)
                .InvokeCommand(_refresh)
                .DisposeWith(disposable);

                const int refreshPeriod = 30;
                _refresh.Select(results => refreshPeriod)
                .StartWith(refreshPeriod)
                .Subscribe(x => RefreshingIn = x)
                .DisposeWith(disposable);

                this.WhenAnyValue(x => x.CanInteract)
                .Skip(1)
                .Where(interact => interact)
                .Select(x => Unit.Default)
                .InvokeCommand(_refresh);
            });
        }